Only remove database log files on shutdown after wallet encryption/rewrite
[novacoin.git] / src / db.cpp
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2011 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.
5
6 #include "headers.h"
7 #include "db.h"
8 #include "net.h"
9 #include <boost/filesystem.hpp>
10 #include <boost/filesystem/fstream.hpp>
11
12 using namespace std;
13 using namespace boost;
14
15
16 unsigned int nWalletDBUpdated;
17 uint64 nAccountingEntryNumber = 0;
18
19
20
21 //
22 // CDB
23 //
24
25 static CCriticalSection cs_db;
26 static bool fDbEnvInit = false;
27 DbEnv dbenv(0);
28 static map<string, int> mapFileUseCount;
29 static map<string, Db*> mapDb;
30
31 static bool fRemoveLogFiles = false;
32 void RemoveLogFilesOnShutdown(bool fIn)
33 {
34     fRemoveLogFiles = fIn;
35 }
36 static void EnvShutdown()
37 {
38     if (!fDbEnvInit)
39         return;
40
41     fDbEnvInit = false;
42     try
43     {
44         dbenv.close(0);
45     }
46     catch (const DbException& e)
47     {
48         printf("EnvShutdown exception: %s (%d)\n", e.what(), e.get_errno());
49     }
50     DbEnv(0).remove(GetDataDir().c_str(), 0);
51
52     if (fRemoveLogFiles)
53     {
54         filesystem::path datadir(GetDataDir());
55         filesystem::directory_iterator it(datadir / "database");
56         while (it != filesystem::directory_iterator())
57         {
58             const filesystem::path& p = it->path();
59 #if BOOST_FILESYSTEM_VERSION >= 3
60             std::string f = p.filename().generic_string();
61 #else
62             std::string f = p.filename();
63 #endif
64             if (f.find("log.") == 0)
65                 filesystem::remove(p);
66             ++it;
67         }
68     }
69 }
70
71 class CDBInit
72 {
73 public:
74     CDBInit()
75     {
76     }
77     ~CDBInit()
78     {
79         EnvShutdown();
80     }
81 }
82 instance_of_cdbinit;
83
84
85 CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL)
86 {
87     int ret;
88     if (pszFile == NULL)
89         return;
90
91     fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
92     bool fCreate = strchr(pszMode, 'c');
93     unsigned int nFlags = DB_THREAD;
94     if (fCreate)
95         nFlags |= DB_CREATE;
96
97     CRITICAL_BLOCK(cs_db)
98     {
99         if (!fDbEnvInit)
100         {
101             if (fShutdown)
102                 return;
103             string strDataDir = GetDataDir();
104             string strLogDir = strDataDir + "/database";
105             filesystem::create_directory(strLogDir.c_str());
106             string strErrorFile = strDataDir + "/db.log";
107             printf("dbenv.open strLogDir=%s strErrorFile=%s\n", strLogDir.c_str(), strErrorFile.c_str());
108
109             dbenv.set_lg_dir(strLogDir.c_str());
110             dbenv.set_lg_max(10000000);
111             dbenv.set_lk_max_locks(10000);
112             dbenv.set_lk_max_objects(10000);
113             dbenv.set_errfile(fopen(strErrorFile.c_str(), "a")); /// debug
114             dbenv.set_flags(DB_AUTO_COMMIT, 1);
115             ret = dbenv.open(strDataDir.c_str(),
116                              DB_CREATE     |
117                              DB_INIT_LOCK  |
118                              DB_INIT_LOG   |
119                              DB_INIT_MPOOL |
120                              DB_INIT_TXN   |
121                              DB_THREAD     |
122                              DB_RECOVER,
123                              S_IRUSR | S_IWUSR);
124             if (ret > 0)
125                 throw runtime_error(strprintf("CDB() : error %d opening database environment", ret));
126             fDbEnvInit = true;
127         }
128
129         strFile = pszFile;
130         ++mapFileUseCount[strFile];
131         pdb = mapDb[strFile];
132         if (pdb == NULL)
133         {
134             pdb = new Db(&dbenv, 0);
135
136             ret = pdb->open(NULL,      // Txn pointer
137                             pszFile,   // Filename
138                             "main",    // Logical db name
139                             DB_BTREE,  // Database type
140                             nFlags,    // Flags
141                             0);
142
143             if (ret > 0)
144             {
145                 delete pdb;
146                 pdb = NULL;
147                 CRITICAL_BLOCK(cs_db)
148                     --mapFileUseCount[strFile];
149                 strFile = "";
150                 throw runtime_error(strprintf("CDB() : can't open database file %s, error %d", pszFile, ret));
151             }
152
153             if (fCreate && !Exists(string("version")))
154             {
155                 bool fTmp = fReadOnly;
156                 fReadOnly = false;
157                 WriteVersion(VERSION);
158                 fReadOnly = fTmp;
159             }
160
161             mapDb[strFile] = pdb;
162         }
163     }
164 }
165
166 void CDB::Close()
167 {
168     if (!pdb)
169         return;
170     if (!vTxn.empty())
171         vTxn.front()->abort();
172     vTxn.clear();
173     pdb = NULL;
174
175     // Flush database activity from memory pool to disk log
176     unsigned int nMinutes = 0;
177     if (fReadOnly)
178         nMinutes = 1;
179     if (strFile == "addr.dat")
180         nMinutes = 2;
181     if (strFile == "blkindex.dat" && IsInitialBlockDownload() && nBestHeight % 500 != 0)
182         nMinutes = 1;
183     dbenv.txn_checkpoint(0, nMinutes, 0);
184
185     CRITICAL_BLOCK(cs_db)
186         --mapFileUseCount[strFile];
187 }
188
189 void static CloseDb(const string& strFile)
190 {
191     CRITICAL_BLOCK(cs_db)
192     {
193         if (mapDb[strFile] != NULL)
194         {
195             // Close the database handle
196             Db* pdb = mapDb[strFile];
197             pdb->close(0);
198             delete pdb;
199             mapDb[strFile] = NULL;
200         }
201     }
202 }
203
204 bool CDB::Rewrite(const string& strFile, const char* pszSkip)
205 {
206     while (!fShutdown)
207     {
208         CRITICAL_BLOCK(cs_db)
209         {
210             if (!mapFileUseCount.count(strFile) || mapFileUseCount[strFile] == 0)
211             {
212                 // Flush log data to the dat file
213                 CloseDb(strFile);
214                 dbenv.txn_checkpoint(0, 0, 0);
215                 dbenv.lsn_reset(strFile.c_str(), 0);
216                 mapFileUseCount.erase(strFile);
217
218                 bool fSuccess = true;
219                 printf("Rewriting %s...\n", strFile.c_str());
220                 string strFileRes = strFile + ".rewrite";
221                 CDB db(strFile.c_str(), "r");
222                 Db* pdbCopy = new Db(&dbenv, 0);
223
224                 int ret = pdbCopy->open(NULL,                 // Txn pointer
225                                         strFileRes.c_str(),   // Filename
226                                         "main",    // Logical db name
227                                         DB_BTREE,  // Database type
228                                         DB_CREATE,    // Flags
229                                         0);
230                 if (ret > 0)
231                 {
232                     printf("Cannot create database file %s\n", strFileRes.c_str());
233                     fSuccess = false;
234                 }
235
236                 Dbc* pcursor = db.GetCursor();
237                 if (pcursor)
238                     while (fSuccess)
239                     {
240                         CDataStream ssKey;
241                         CDataStream ssValue;
242                         int ret = db.ReadAtCursor(pcursor, ssKey, ssValue, DB_NEXT);
243                         if (ret == DB_NOTFOUND)
244                         {
245                             pcursor->close();
246                             break;
247                         }
248                         else if (ret != 0)
249                         {
250                             pcursor->close();
251                             fSuccess = false;
252                             break;
253                         }
254                         if (pszSkip &&
255                             strncmp(&ssKey[0], pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0)
256                             continue;
257                         if (strncmp(&ssKey[0], "\x07version", 8) == 0)
258                         {
259                             // Update version:
260                             ssValue.clear();
261                             ssValue << VERSION;
262                         }
263                         Dbt datKey(&ssKey[0], ssKey.size());
264                         Dbt datValue(&ssValue[0], ssValue.size());
265                         int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE);
266                         if (ret2 > 0)
267                             fSuccess = false;
268                     }
269                 if (fSuccess)
270                 {
271                     db.Close();
272                     CloseDb(strFile);
273                     if (pdbCopy->close(0))
274                         fSuccess = false;
275                     delete pdbCopy;
276                 }
277                 if (fSuccess)
278                 {
279                     Db dbA(&dbenv, 0);
280                     if (dbA.remove(strFile.c_str(), NULL, 0))
281                         fSuccess = false;
282                     Db dbB(&dbenv, 0);
283                     if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0))
284                         fSuccess = false;
285                 }
286                 if (!fSuccess)
287                     printf("Rewriting of %s FAILED!\n", strFileRes.c_str());
288                 return fSuccess;
289             }
290         }
291         Sleep(100);
292     }
293     return false;
294 }
295
296
297 void DBFlush(bool fShutdown)
298 {
299     // Flush log data to the actual data file
300     //  on all files that are not in use
301     printf("DBFlush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started");
302     if (!fDbEnvInit)
303         return;
304     CRITICAL_BLOCK(cs_db)
305     {
306         map<string, int>::iterator mi = mapFileUseCount.begin();
307         while (mi != mapFileUseCount.end())
308         {
309             string strFile = (*mi).first;
310             int nRefCount = (*mi).second;
311             printf("%s refcount=%d\n", strFile.c_str(), nRefCount);
312             if (nRefCount == 0)
313             {
314                 // Move log data to the dat file
315                 CloseDb(strFile);
316                 dbenv.txn_checkpoint(0, 0, 0);
317                 printf("%s flush\n", strFile.c_str());
318                 dbenv.lsn_reset(strFile.c_str(), 0);
319                 mapFileUseCount.erase(mi++);
320             }
321             else
322                 mi++;
323         }
324         if (fShutdown)
325         {
326             char** listp;
327             if (mapFileUseCount.empty())
328             {
329                 dbenv.log_archive(&listp, DB_ARCH_REMOVE);
330                 EnvShutdown();
331             }
332         }
333     }
334 }
335
336
337
338
339
340
341 //
342 // CTxDB
343 //
344
345 bool CTxDB::ReadTxIndex(uint256 hash, CTxIndex& txindex)
346 {
347     assert(!fClient);
348     txindex.SetNull();
349     return Read(make_pair(string("tx"), hash), txindex);
350 }
351
352 bool CTxDB::UpdateTxIndex(uint256 hash, const CTxIndex& txindex)
353 {
354     assert(!fClient);
355     return Write(make_pair(string("tx"), hash), txindex);
356 }
357
358 bool CTxDB::AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight)
359 {
360     assert(!fClient);
361
362     // Add to tx index
363     uint256 hash = tx.GetHash();
364     CTxIndex txindex(pos, tx.vout.size());
365     return Write(make_pair(string("tx"), hash), txindex);
366 }
367
368 bool CTxDB::EraseTxIndex(const CTransaction& tx)
369 {
370     assert(!fClient);
371     uint256 hash = tx.GetHash();
372
373     return Erase(make_pair(string("tx"), hash));
374 }
375
376 bool CTxDB::ContainsTx(uint256 hash)
377 {
378     assert(!fClient);
379     return Exists(make_pair(string("tx"), hash));
380 }
381
382 bool CTxDB::ReadOwnerTxes(uint160 hash160, int nMinHeight, vector<CTransaction>& vtx)
383 {
384     assert(!fClient);
385     vtx.clear();
386
387     // Get cursor
388     Dbc* pcursor = GetCursor();
389     if (!pcursor)
390         return false;
391
392     unsigned int fFlags = DB_SET_RANGE;
393     loop
394     {
395         // Read next record
396         CDataStream ssKey;
397         if (fFlags == DB_SET_RANGE)
398             ssKey << string("owner") << hash160 << CDiskTxPos(0, 0, 0);
399         CDataStream ssValue;
400         int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
401         fFlags = DB_NEXT;
402         if (ret == DB_NOTFOUND)
403             break;
404         else if (ret != 0)
405         {
406             pcursor->close();
407             return false;
408         }
409
410         // Unserialize
411         string strType;
412         uint160 hashItem;
413         CDiskTxPos pos;
414         ssKey >> strType >> hashItem >> pos;
415         int nItemHeight;
416         ssValue >> nItemHeight;
417
418         // Read transaction
419         if (strType != "owner" || hashItem != hash160)
420             break;
421         if (nItemHeight >= nMinHeight)
422         {
423             vtx.resize(vtx.size()+1);
424             if (!vtx.back().ReadFromDisk(pos))
425             {
426                 pcursor->close();
427                 return false;
428             }
429         }
430     }
431
432     pcursor->close();
433     return true;
434 }
435
436 bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex)
437 {
438     assert(!fClient);
439     tx.SetNull();
440     if (!ReadTxIndex(hash, txindex))
441         return false;
442     return (tx.ReadFromDisk(txindex.pos));
443 }
444
445 bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx)
446 {
447     CTxIndex txindex;
448     return ReadDiskTx(hash, tx, txindex);
449 }
450
451 bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex)
452 {
453     return ReadDiskTx(outpoint.hash, tx, txindex);
454 }
455
456 bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx)
457 {
458     CTxIndex txindex;
459     return ReadDiskTx(outpoint.hash, tx, txindex);
460 }
461
462 bool CTxDB::WriteBlockIndex(const CDiskBlockIndex& blockindex)
463 {
464     return Write(make_pair(string("blockindex"), blockindex.GetBlockHash()), blockindex);
465 }
466
467 bool CTxDB::EraseBlockIndex(uint256 hash)
468 {
469     return Erase(make_pair(string("blockindex"), hash));
470 }
471
472 bool CTxDB::ReadHashBestChain(uint256& hashBestChain)
473 {
474     return Read(string("hashBestChain"), hashBestChain);
475 }
476
477 bool CTxDB::WriteHashBestChain(uint256 hashBestChain)
478 {
479     return Write(string("hashBestChain"), hashBestChain);
480 }
481
482 bool CTxDB::ReadBestInvalidWork(CBigNum& bnBestInvalidWork)
483 {
484     return Read(string("bnBestInvalidWork"), bnBestInvalidWork);
485 }
486
487 bool CTxDB::WriteBestInvalidWork(CBigNum bnBestInvalidWork)
488 {
489     return Write(string("bnBestInvalidWork"), bnBestInvalidWork);
490 }
491
492 CBlockIndex static * InsertBlockIndex(uint256 hash)
493 {
494     if (hash == 0)
495         return NULL;
496
497     // Return existing
498     map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash);
499     if (mi != mapBlockIndex.end())
500         return (*mi).second;
501
502     // Create new
503     CBlockIndex* pindexNew = new CBlockIndex();
504     if (!pindexNew)
505         throw runtime_error("LoadBlockIndex() : new CBlockIndex failed");
506     mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
507     pindexNew->phashBlock = &((*mi).first);
508
509     return pindexNew;
510 }
511
512 bool CTxDB::LoadBlockIndex()
513 {
514     // Get database cursor
515     Dbc* pcursor = GetCursor();
516     if (!pcursor)
517         return false;
518
519     // Load mapBlockIndex
520     unsigned int fFlags = DB_SET_RANGE;
521     loop
522     {
523         // Read next record
524         CDataStream ssKey;
525         if (fFlags == DB_SET_RANGE)
526             ssKey << make_pair(string("blockindex"), uint256(0));
527         CDataStream ssValue;
528         int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
529         fFlags = DB_NEXT;
530         if (ret == DB_NOTFOUND)
531             break;
532         else if (ret != 0)
533             return false;
534
535         // Unserialize
536         string strType;
537         ssKey >> strType;
538         if (strType == "blockindex")
539         {
540             CDiskBlockIndex diskindex;
541             ssValue >> diskindex;
542
543             // Construct block index object
544             CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash());
545             pindexNew->pprev          = InsertBlockIndex(diskindex.hashPrev);
546             pindexNew->pnext          = InsertBlockIndex(diskindex.hashNext);
547             pindexNew->nFile          = diskindex.nFile;
548             pindexNew->nBlockPos      = diskindex.nBlockPos;
549             pindexNew->nHeight        = diskindex.nHeight;
550             pindexNew->nVersion       = diskindex.nVersion;
551             pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
552             pindexNew->nTime          = diskindex.nTime;
553             pindexNew->nBits          = diskindex.nBits;
554             pindexNew->nNonce         = diskindex.nNonce;
555
556             // Watch for genesis block
557             if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == hashGenesisBlock)
558                 pindexGenesisBlock = pindexNew;
559
560             if (!pindexNew->CheckIndex())
561                 return error("LoadBlockIndex() : CheckIndex failed at %d", pindexNew->nHeight);
562         }
563         else
564         {
565             break;
566         }
567     }
568     pcursor->close();
569
570     // Calculate bnChainWork
571     vector<pair<int, CBlockIndex*> > vSortedByHeight;
572     vSortedByHeight.reserve(mapBlockIndex.size());
573     BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex)
574     {
575         CBlockIndex* pindex = item.second;
576         vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex));
577     }
578     sort(vSortedByHeight.begin(), vSortedByHeight.end());
579     BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight)
580     {
581         CBlockIndex* pindex = item.second;
582         pindex->bnChainWork = (pindex->pprev ? pindex->pprev->bnChainWork : 0) + pindex->GetBlockWork();
583     }
584
585     // Load hashBestChain pointer to end of best chain
586     if (!ReadHashBestChain(hashBestChain))
587     {
588         if (pindexGenesisBlock == NULL)
589             return true;
590         return error("CTxDB::LoadBlockIndex() : hashBestChain not loaded");
591     }
592     if (!mapBlockIndex.count(hashBestChain))
593         return error("CTxDB::LoadBlockIndex() : hashBestChain not found in the block index");
594     pindexBest = mapBlockIndex[hashBestChain];
595     nBestHeight = pindexBest->nHeight;
596     bnBestChainWork = pindexBest->bnChainWork;
597     printf("LoadBlockIndex(): hashBestChain=%s  height=%d\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight);
598
599     // Load bnBestInvalidWork, OK if it doesn't exist
600     ReadBestInvalidWork(bnBestInvalidWork);
601
602     // Verify blocks in the best chain
603     CBlockIndex* pindexFork = NULL;
604     for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev)
605     {
606         if (pindex->nHeight < nBestHeight-2500 && !mapArgs.count("-checkblocks"))
607             break;
608         CBlock block;
609         if (!block.ReadFromDisk(pindex))
610             return error("LoadBlockIndex() : block.ReadFromDisk failed");
611         if (!block.CheckBlock())
612         {
613             printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
614             pindexFork = pindex->pprev;
615         }
616     }
617     if (pindexFork)
618     {
619         // Reorg back to the fork
620         printf("LoadBlockIndex() : *** moving best chain pointer back to block %d\n", pindexFork->nHeight);
621         CBlock block;
622         if (!block.ReadFromDisk(pindexFork))
623             return error("LoadBlockIndex() : block.ReadFromDisk failed");
624         CTxDB txdb;
625         block.SetBestChain(txdb, pindexFork);
626     }
627
628     return true;
629 }
630
631
632
633
634
635 //
636 // CAddrDB
637 //
638
639 bool CAddrDB::WriteAddress(const CAddress& addr)
640 {
641     return Write(make_pair(string("addr"), addr.GetKey()), addr);
642 }
643
644 bool CAddrDB::EraseAddress(const CAddress& addr)
645 {
646     return Erase(make_pair(string("addr"), addr.GetKey()));
647 }
648
649 bool CAddrDB::LoadAddresses()
650 {
651     CRITICAL_BLOCK(cs_mapAddresses)
652     {
653         // Load user provided addresses
654         CAutoFile filein = fopen((GetDataDir() + "/addr.txt").c_str(), "rt");
655         if (filein)
656         {
657             try
658             {
659                 char psz[1000];
660                 while (fgets(psz, sizeof(psz), filein))
661                 {
662                     CAddress addr(psz, false, NODE_NETWORK);
663                     addr.nTime = 0; // so it won't relay unless successfully connected
664                     if (addr.IsValid())
665                         AddAddress(addr);
666                 }
667             }
668             catch (...) { }
669         }
670
671         // Get cursor
672         Dbc* pcursor = GetCursor();
673         if (!pcursor)
674             return false;
675
676         loop
677         {
678             // Read next record
679             CDataStream ssKey;
680             CDataStream ssValue;
681             int ret = ReadAtCursor(pcursor, ssKey, ssValue);
682             if (ret == DB_NOTFOUND)
683                 break;
684             else if (ret != 0)
685                 return false;
686
687             // Unserialize
688             string strType;
689             ssKey >> strType;
690             if (strType == "addr")
691             {
692                 CAddress addr;
693                 ssValue >> addr;
694                 mapAddresses.insert(make_pair(addr.GetKey(), addr));
695             }
696         }
697         pcursor->close();
698
699         printf("Loaded %d addresses\n", mapAddresses.size());
700     }
701
702     return true;
703 }
704
705 bool LoadAddresses()
706 {
707     return CAddrDB("cr+").LoadAddresses();
708 }
709
710
711
712
713 //
714 // CWalletDB
715 //
716
717 bool CWalletDB::WriteName(const string& strAddress, const string& strName)
718 {
719     nWalletDBUpdated++;
720     return Write(make_pair(string("name"), strAddress), strName);
721 }
722
723 bool CWalletDB::EraseName(const string& strAddress)
724 {
725     // This should only be used for sending addresses, never for receiving addresses,
726     // receiving addresses must always have an address book entry if they're not change return.
727     nWalletDBUpdated++;
728     return Erase(make_pair(string("name"), strAddress));
729 }
730
731 bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account)
732 {
733     account.SetNull();
734     return Read(make_pair(string("acc"), strAccount), account);
735 }
736
737 bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account)
738 {
739     return Write(make_pair(string("acc"), strAccount), account);
740 }
741
742 bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry)
743 {
744     return Write(boost::make_tuple(string("acentry"), acentry.strAccount, ++nAccountingEntryNumber), acentry);
745 }
746
747 int64 CWalletDB::GetAccountCreditDebit(const string& strAccount)
748 {
749     list<CAccountingEntry> entries;
750     ListAccountCreditDebit(strAccount, entries);
751
752     int64 nCreditDebit = 0;
753     BOOST_FOREACH (const CAccountingEntry& entry, entries)
754         nCreditDebit += entry.nCreditDebit;
755
756     return nCreditDebit;
757 }
758
759 void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountingEntry>& entries)
760 {
761     bool fAllAccounts = (strAccount == "*");
762
763     Dbc* pcursor = GetCursor();
764     if (!pcursor)
765         throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor");
766     unsigned int fFlags = DB_SET_RANGE;
767     loop
768     {
769         // Read next record
770         CDataStream ssKey;
771         if (fFlags == DB_SET_RANGE)
772             ssKey << boost::make_tuple(string("acentry"), (fAllAccounts? string("") : strAccount), uint64(0));
773         CDataStream ssValue;
774         int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
775         fFlags = DB_NEXT;
776         if (ret == DB_NOTFOUND)
777             break;
778         else if (ret != 0)
779         {
780             pcursor->close();
781             throw runtime_error("CWalletDB::ListAccountCreditDebit() : error scanning DB");
782         }
783
784         // Unserialize
785         string strType;
786         ssKey >> strType;
787         if (strType != "acentry")
788             break;
789         CAccountingEntry acentry;
790         ssKey >> acentry.strAccount;
791         if (!fAllAccounts && acentry.strAccount != strAccount)
792             break;
793
794         ssValue >> acentry;
795         entries.push_back(acentry);
796     }
797
798     pcursor->close();
799 }
800
801
802 int CWalletDB::LoadWallet(CWallet* pwallet)
803 {
804     pwallet->vchDefaultKey.clear();
805     int nFileVersion = 0;
806     vector<uint256> vWalletUpgrade;
807     bool fIsEncrypted = false;
808
809     // Modify defaults
810 #ifndef __WXMSW__
811     // Tray icon sometimes disappears on 9.10 karmic koala 64-bit, leaving no way to access the program
812     fMinimizeToTray = false;
813     fMinimizeOnClose = false;
814 #endif
815
816     //// todo: shouldn't we catch exceptions and try to recover and continue?
817     CRITICAL_BLOCK(pwallet->cs_wallet)
818     {
819         // Get cursor
820         Dbc* pcursor = GetCursor();
821         if (!pcursor)
822             return DB_CORRUPT;
823
824         loop
825         {
826             // Read next record
827             CDataStream ssKey;
828             CDataStream ssValue;
829             int ret = ReadAtCursor(pcursor, ssKey, ssValue);
830             if (ret == DB_NOTFOUND)
831                 break;
832             else if (ret != 0)
833                 return DB_CORRUPT;
834
835             // Unserialize
836             // Taking advantage of the fact that pair serialization
837             // is just the two items serialized one after the other
838             string strType;
839             ssKey >> strType;
840             if (strType == "name")
841             {
842                 string strAddress;
843                 ssKey >> strAddress;
844                 ssValue >> pwallet->mapAddressBook[strAddress];
845             }
846             else if (strType == "tx")
847             {
848                 uint256 hash;
849                 ssKey >> hash;
850                 CWalletTx& wtx = pwallet->mapWallet[hash];
851                 ssValue >> wtx;
852                 wtx.pwallet = pwallet;
853
854                 if (wtx.GetHash() != hash)
855                     printf("Error in wallet.dat, hash mismatch\n");
856
857                 // Undo serialize changes in 31600
858                 if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
859                 {
860                     if (!ssValue.empty())
861                     {
862                         char fTmp;
863                         char fUnused;
864                         ssValue >> fTmp >> fUnused >> wtx.strFromAccount;
865                         printf("LoadWallet() upgrading tx ver=%d %d '%s' %s\n", wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str());
866                         wtx.fTimeReceivedIsTxTime = fTmp;
867                     }
868                     else
869                     {
870                         printf("LoadWallet() repairing tx ver=%d %s\n", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str());
871                         wtx.fTimeReceivedIsTxTime = 0;
872                     }
873                     vWalletUpgrade.push_back(hash);
874                 }
875
876                 //// debug print
877                 //printf("LoadWallet  %s\n", wtx.GetHash().ToString().c_str());
878                 //printf(" %12I64d  %s  %s  %s\n",
879                 //    wtx.vout[0].nValue,
880                 //    DateTimeStrFormat("%x %H:%M:%S", wtx.GetBlockTime()).c_str(),
881                 //    wtx.hashBlock.ToString().substr(0,20).c_str(),
882                 //    wtx.mapValue["message"].c_str());
883             }
884             else if (strType == "acentry")
885             {
886                 string strAccount;
887                 ssKey >> strAccount;
888                 uint64 nNumber;
889                 ssKey >> nNumber;
890                 if (nNumber > nAccountingEntryNumber)
891                     nAccountingEntryNumber = nNumber;
892             }
893             else if (strType == "key" || strType == "wkey")
894             {
895                 vector<unsigned char> vchPubKey;
896                 ssKey >> vchPubKey;
897                 CKey key;
898                 if (strType == "key")
899                 {
900                     CPrivKey pkey;
901                     ssValue >> pkey;
902                     key.SetPrivKey(pkey);
903                 }
904                 else
905                 {
906                     CWalletKey wkey;
907                     ssValue >> wkey;
908                     key.SetPrivKey(wkey.vchPrivKey);
909                 }
910                 if (!pwallet->LoadKey(key))
911                     return DB_CORRUPT;
912             }
913             else if (strType == "mkey")
914             {
915                 unsigned int nID;
916                 ssKey >> nID;
917                 CMasterKey kMasterKey;
918                 ssValue >> kMasterKey;
919                 if(pwallet->mapMasterKeys.count(nID) != 0)
920                     return DB_CORRUPT;
921                 pwallet->mapMasterKeys[nID] = kMasterKey;
922                 if (pwallet->nMasterKeyMaxID < nID)
923                     pwallet->nMasterKeyMaxID = nID;
924             }
925             else if (strType == "ckey")
926             {
927                 vector<unsigned char> vchPubKey;
928                 ssKey >> vchPubKey;
929                 vector<unsigned char> vchPrivKey;
930                 ssValue >> vchPrivKey;
931                 if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey))
932                     return DB_CORRUPT;
933                 fIsEncrypted = true;
934             }
935             else if (strType == "defaultkey")
936             {
937                 ssValue >> pwallet->vchDefaultKey;
938             }
939             else if (strType == "pool")
940             {
941                 int64 nIndex;
942                 ssKey >> nIndex;
943                 pwallet->setKeyPool.insert(nIndex);
944             }
945             else if (strType == "version")
946             {
947                 ssValue >> nFileVersion;
948                 if (nFileVersion == 10300)
949                     nFileVersion = 300;
950             }
951             else if (strType == "setting")
952             {
953                 string strKey;
954                 ssKey >> strKey;
955
956                 // Options
957 #ifndef GUI
958                 if (strKey == "fGenerateBitcoins")  ssValue >> fGenerateBitcoins;
959 #endif
960                 if (strKey == "nTransactionFee")    ssValue >> nTransactionFee;
961                 if (strKey == "fLimitProcessors")   ssValue >> fLimitProcessors;
962                 if (strKey == "nLimitProcessors")   ssValue >> nLimitProcessors;
963                 if (strKey == "fMinimizeToTray")    ssValue >> fMinimizeToTray;
964                 if (strKey == "fMinimizeOnClose")   ssValue >> fMinimizeOnClose;
965                 if (strKey == "fUseProxy")          ssValue >> fUseProxy;
966                 if (strKey == "addrProxy")          ssValue >> addrProxy;
967                 if (fHaveUPnP && strKey == "fUseUPnP")           ssValue >> fUseUPnP;
968             }
969             else if (strType == "minversion")
970             {
971                 int nMinVersion = 0;
972                 ssValue >> nMinVersion;
973                 if (nMinVersion > VERSION)
974                     return DB_TOO_NEW;
975             }
976         }
977         pcursor->close();
978     }
979
980     BOOST_FOREACH(uint256 hash, vWalletUpgrade)
981         WriteTx(hash, pwallet->mapWallet[hash]);
982
983     printf("nFileVersion = %d\n", nFileVersion);
984     printf("fGenerateBitcoins = %d\n", fGenerateBitcoins);
985     printf("nTransactionFee = %"PRI64d"\n", nTransactionFee);
986     printf("fMinimizeToTray = %d\n", fMinimizeToTray);
987     printf("fMinimizeOnClose = %d\n", fMinimizeOnClose);
988     printf("fUseProxy = %d\n", fUseProxy);
989     printf("addrProxy = %s\n", addrProxy.ToString().c_str());
990     if (fHaveUPnP)
991         printf("fUseUPnP = %d\n", fUseUPnP);
992
993
994     // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
995     if (fIsEncrypted && (nFileVersion == 40000 || nFileVersion == 50000))
996         return DB_NEED_REWRITE;
997
998     if (nFileVersion < VERSION) // Update
999     {
1000         // Get rid of old debug.log file in current directory
1001         if (nFileVersion <= 105 && !pszSetDataDir[0])
1002             unlink("debug.log");
1003
1004         WriteVersion(VERSION);
1005     }
1006
1007     return DB_LOAD_OK;
1008 }
1009
1010 void ThreadFlushWalletDB(void* parg)
1011 {
1012     const string& strFile = ((const string*)parg)[0];
1013     static bool fOneThread;
1014     if (fOneThread)
1015         return;
1016     fOneThread = true;
1017     if (mapArgs.count("-noflushwallet"))
1018         return;
1019
1020     unsigned int nLastSeen = nWalletDBUpdated;
1021     unsigned int nLastFlushed = nWalletDBUpdated;
1022     int64 nLastWalletUpdate = GetTime();
1023     while (!fShutdown)
1024     {
1025         Sleep(500);
1026
1027         if (nLastSeen != nWalletDBUpdated)
1028         {
1029             nLastSeen = nWalletDBUpdated;
1030             nLastWalletUpdate = GetTime();
1031         }
1032
1033         if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2)
1034         {
1035             TRY_CRITICAL_BLOCK(cs_db)
1036             {
1037                 // Don't do this if any databases are in use
1038                 int nRefCount = 0;
1039                 map<string, int>::iterator mi = mapFileUseCount.begin();
1040                 while (mi != mapFileUseCount.end())
1041                 {
1042                     nRefCount += (*mi).second;
1043                     mi++;
1044                 }
1045
1046                 if (nRefCount == 0 && !fShutdown)
1047                 {
1048                     map<string, int>::iterator mi = mapFileUseCount.find(strFile);
1049                     if (mi != mapFileUseCount.end())
1050                     {
1051                         printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str());
1052                         printf("Flushing wallet.dat\n");
1053                         nLastFlushed = nWalletDBUpdated;
1054                         int64 nStart = GetTimeMillis();
1055
1056                         // Flush wallet.dat so it's self contained
1057                         CloseDb(strFile);
1058                         dbenv.txn_checkpoint(0, 0, 0);
1059                         dbenv.lsn_reset(strFile.c_str(), 0);
1060
1061                         mapFileUseCount.erase(mi++);
1062                         printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart);
1063                     }
1064                 }
1065             }
1066         }
1067     }
1068 }
1069
1070 bool BackupWallet(const CWallet& wallet, const string& strDest)
1071 {
1072     if (!wallet.fFileBacked)
1073         return false;
1074     while (!fShutdown)
1075     {
1076         CRITICAL_BLOCK(cs_db)
1077         {
1078             if (!mapFileUseCount.count(wallet.strWalletFile) || mapFileUseCount[wallet.strWalletFile] == 0)
1079             {
1080                 // Flush log data to the dat file
1081                 CloseDb(wallet.strWalletFile);
1082                 dbenv.txn_checkpoint(0, 0, 0);
1083                 dbenv.lsn_reset(wallet.strWalletFile.c_str(), 0);
1084                 mapFileUseCount.erase(wallet.strWalletFile);
1085
1086                 // Copy wallet.dat
1087                 filesystem::path pathSrc(GetDataDir() + "/" + wallet.strWalletFile);
1088                 filesystem::path pathDest(strDest);
1089                 if (filesystem::is_directory(pathDest))
1090                     pathDest = pathDest / wallet.strWalletFile;
1091 #if BOOST_VERSION >= 104000
1092                 filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists);
1093 #else
1094                 filesystem::copy_file(pathSrc, pathDest);
1095 #endif
1096                 printf("copied wallet.dat to %s\n", pathDest.string().c_str());
1097
1098                 return true;
1099             }
1100         }
1101         Sleep(100);
1102     }
1103     return false;
1104 }