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 license.txt or http://www.opensource.org/licenses/mit-license.php.
8 #include <boost/filesystem.hpp>
11 using namespace boost;
14 static uint64 nAccountingEntryNumber = 0;
16 extern CCriticalSection cs_db;
17 extern map<string, int> mapFileUseCount;
18 extern void CloseDb(const string& strFile);
24 bool CWalletDB::WriteName(const string& strAddress, const string& strName)
27 return Write(make_pair(string("name"), strAddress), strName);
30 bool CWalletDB::EraseName(const string& strAddress)
32 // This should only be used for sending addresses, never for receiving addresses,
33 // receiving addresses must always have an address book entry if they're not change return.
35 return Erase(make_pair(string("name"), strAddress));
38 bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account)
41 return Read(make_pair(string("acc"), strAccount), account);
44 bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account)
46 return Write(make_pair(string("acc"), strAccount), account);
49 bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry)
51 return Write(boost::make_tuple(string("acentry"), acentry.strAccount, ++nAccountingEntryNumber), acentry);
54 int64 CWalletDB::GetAccountCreditDebit(const string& strAccount)
56 list<CAccountingEntry> entries;
57 ListAccountCreditDebit(strAccount, entries);
59 int64 nCreditDebit = 0;
60 BOOST_FOREACH (const CAccountingEntry& entry, entries)
61 nCreditDebit += entry.nCreditDebit;
66 void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountingEntry>& entries)
68 bool fAllAccounts = (strAccount == "*");
70 Dbc* pcursor = GetCursor();
72 throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor");
73 unsigned int fFlags = DB_SET_RANGE;
77 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
78 if (fFlags == DB_SET_RANGE)
79 ssKey << boost::make_tuple(string("acentry"), (fAllAccounts? string("") : strAccount), uint64(0));
80 CDataStream ssValue(SER_DISK, CLIENT_VERSION);
81 int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
83 if (ret == DB_NOTFOUND)
88 throw runtime_error("CWalletDB::ListAccountCreditDebit() : error scanning DB");
94 if (strType != "acentry")
96 CAccountingEntry acentry;
97 ssKey >> acentry.strAccount;
98 if (!fAllAccounts && acentry.strAccount != strAccount)
102 entries.push_back(acentry);
109 int CWalletDB::LoadWallet(CWallet* pwallet)
111 pwallet->vchDefaultKey.clear();
112 int nFileVersion = 0;
113 vector<uint256> vWalletUpgrade;
114 bool fIsEncrypted = false;
116 //// todo: shouldn't we catch exceptions and try to recover and continue?
118 LOCK(pwallet->cs_wallet);
120 if (Read((string)"minversion", nMinVersion))
122 if (nMinVersion > CLIENT_VERSION)
124 pwallet->LoadMinVersion(nMinVersion);
128 Dbc* pcursor = GetCursor();
131 printf("Error getting wallet database cursor\n");
138 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
139 CDataStream ssValue(SER_DISK, CLIENT_VERSION);
140 int ret = ReadAtCursor(pcursor, ssKey, ssValue);
141 if (ret == DB_NOTFOUND)
145 printf("Error reading next record from wallet database\n");
150 // Taking advantage of the fact that pair serialization
151 // is just the two items serialized one after the other
154 if (strType == "name")
158 ssValue >> pwallet->mapAddressBook[strAddress];
160 else if (strType == "tx")
164 CWalletTx& wtx = pwallet->mapWallet[hash];
166 wtx.BindWallet(pwallet);
168 if (wtx.GetHash() != hash)
169 printf("Error in wallet.dat, hash mismatch\n");
171 // Undo serialize changes in 31600
172 if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
174 if (!ssValue.empty())
178 ssValue >> fTmp >> fUnused >> wtx.strFromAccount;
179 printf("LoadWallet() upgrading tx ver=%d %d '%s' %s\n", wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str());
180 wtx.fTimeReceivedIsTxTime = fTmp;
184 printf("LoadWallet() repairing tx ver=%d %s\n", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str());
185 wtx.fTimeReceivedIsTxTime = 0;
187 vWalletUpgrade.push_back(hash);
191 //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str());
192 //printf(" %12I64d %s %s %s\n",
193 // wtx.vout[0].nValue,
194 // DateTimeStrFormat("%x %H:%M:%S", wtx.GetBlockTime()).c_str(),
195 // wtx.hashBlock.ToString().substr(0,20).c_str(),
196 // wtx.mapValue["message"].c_str());
198 else if (strType == "acentry")
204 if (nNumber > nAccountingEntryNumber)
205 nAccountingEntryNumber = nNumber;
207 else if (strType == "key" || strType == "wkey")
209 vector<unsigned char> vchPubKey;
212 if (strType == "key")
216 key.SetPubKey(vchPubKey);
217 key.SetPrivKey(pkey);
218 if (key.GetPubKey() != vchPubKey)
220 printf("Error reading wallet database: CPrivKey pubkey inconsistency\n");
225 printf("Error reading wallet database: invalid CPrivKey\n");
233 key.SetPubKey(vchPubKey);
234 key.SetPrivKey(wkey.vchPrivKey);
235 if (key.GetPubKey() != vchPubKey)
237 printf("Error reading wallet database: CWalletKey pubkey inconsistency\n");
242 printf("Error reading wallet database: invalid CWalletKey\n");
246 if (!pwallet->LoadKey(key))
248 printf("Error reading wallet database: LoadKey failed\n");
252 else if (strType == "mkey")
256 CMasterKey kMasterKey;
257 ssValue >> kMasterKey;
258 if(pwallet->mapMasterKeys.count(nID) != 0)
260 printf("Error reading wallet database: duplicate CMasterKey id %u\n", nID);
263 pwallet->mapMasterKeys[nID] = kMasterKey;
264 if (pwallet->nMasterKeyMaxID < nID)
265 pwallet->nMasterKeyMaxID = nID;
267 else if (strType == "ckey")
269 vector<unsigned char> vchPubKey;
271 vector<unsigned char> vchPrivKey;
272 ssValue >> vchPrivKey;
273 if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey))
275 printf("Error reading wallet database: LoadCryptedKey failed\n");
280 else if (strType == "defaultkey")
282 ssValue >> pwallet->vchDefaultKey;
284 else if (strType == "pool")
288 pwallet->setKeyPool.insert(nIndex);
290 else if (strType == "version")
292 ssValue >> nFileVersion;
293 if (nFileVersion == 10300)
296 else if (strType == "cscript")
302 if (!pwallet->LoadCScript(script))
304 printf("Error reading wallet database: LoadCScript failed\n");
312 BOOST_FOREACH(uint256 hash, vWalletUpgrade)
313 WriteTx(hash, pwallet->mapWallet[hash]);
315 printf("nFileVersion = %d\n", nFileVersion);
318 // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
319 if (fIsEncrypted && (nFileVersion == 40000 || nFileVersion == 50000))
320 return DB_NEED_REWRITE;
322 if (nFileVersion < CLIENT_VERSION) // Update
323 WriteVersion(CLIENT_VERSION);
328 void ThreadFlushWalletDB(void* parg)
330 const string& strFile = ((const string*)parg)[0];
331 static bool fOneThread;
335 if (!GetBoolArg("-flushwallet", true))
338 unsigned int nLastSeen = nWalletDBUpdated;
339 unsigned int nLastFlushed = nWalletDBUpdated;
340 int64 nLastWalletUpdate = GetTime();
345 if (nLastSeen != nWalletDBUpdated)
347 nLastSeen = nWalletDBUpdated;
348 nLastWalletUpdate = GetTime();
351 if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2)
353 TRY_LOCK(cs_db,lockDb);
356 // Don't do this if any databases are in use
358 map<string, int>::iterator mi = mapFileUseCount.begin();
359 while (mi != mapFileUseCount.end())
361 nRefCount += (*mi).second;
365 if (nRefCount == 0 && !fShutdown)
367 map<string, int>::iterator mi = mapFileUseCount.find(strFile);
368 if (mi != mapFileUseCount.end())
370 printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str());
371 printf("Flushing wallet.dat\n");
372 nLastFlushed = nWalletDBUpdated;
373 int64 nStart = GetTimeMillis();
375 // Flush wallet.dat so it's self contained
377 dbenv.txn_checkpoint(0, 0, 0);
378 dbenv.lsn_reset(strFile.c_str(), 0);
380 mapFileUseCount.erase(mi++);
381 printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart);
389 bool BackupWallet(const CWallet& wallet, const string& strDest)
391 if (!wallet.fFileBacked)
397 if (!mapFileUseCount.count(wallet.strWalletFile) || mapFileUseCount[wallet.strWalletFile] == 0)
399 // Flush log data to the dat file
400 CloseDb(wallet.strWalletFile);
401 dbenv.txn_checkpoint(0, 0, 0);
402 dbenv.lsn_reset(wallet.strWalletFile.c_str(), 0);
403 mapFileUseCount.erase(wallet.strWalletFile);
406 filesystem::path pathSrc = GetDataDir() / wallet.strWalletFile;
407 filesystem::path pathDest(strDest);
408 if (filesystem::is_directory(pathDest))
409 pathDest /= wallet.strWalletFile;
412 #if BOOST_VERSION >= 104000
413 filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists);
415 filesystem::copy_file(pathSrc, pathDest);
417 printf("copied wallet.dat to %s\n", pathDest.string().c_str());
419 } catch(const filesystem::filesystem_error &e) {
420 printf("error copying wallet.dat to %s - %s\n", pathDest.string().c_str(), e.what());