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         // Get cursor
654         Dbc* pcursor = GetCursor();
655         if (!pcursor)
656             return false;
657
658         loop
659         {
660             // Read next record
661             CDataStream ssKey;
662             CDataStream ssValue;
663             int ret = ReadAtCursor(pcursor, ssKey, ssValue);
664             if (ret == DB_NOTFOUND)
665                 break;
666             else if (ret != 0)
667                 return false;
668
669             // Unserialize
670             string strType;
671             ssKey >> strType;
672             if (strType == "addr")
673             {
674                 CAddress addr;
675                 ssValue >> addr;
676                 mapAddresses.insert(make_pair(addr.GetKey(), addr));
677             }
678         }
679         pcursor->close();
680
681         printf("Loaded %d addresses\n", mapAddresses.size());
682     }
683
684     return true;
685 }
686
687 bool LoadAddresses()
688 {
689     return CAddrDB("cr+").LoadAddresses();
690 }
691
692
693
694
695 //
696 // CWalletDB
697 //
698
699 bool CWalletDB::WriteName(const string& strAddress, const string& strName)
700 {
701     nWalletDBUpdated++;
702     return Write(make_pair(string("name"), strAddress), strName);
703 }
704
705 bool CWalletDB::EraseName(const string& strAddress)
706 {
707     // This should only be used for sending addresses, never for receiving addresses,
708     // receiving addresses must always have an address book entry if they're not change return.
709     nWalletDBUpdated++;
710     return Erase(make_pair(string("name"), strAddress));
711 }
712
713 bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account)
714 {
715     account.SetNull();
716     return Read(make_pair(string("acc"), strAccount), account);
717 }
718
719 bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account)
720 {
721     return Write(make_pair(string("acc"), strAccount), account);
722 }
723
724 bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry)
725 {
726     return Write(boost::make_tuple(string("acentry"), acentry.strAccount, ++nAccountingEntryNumber), acentry);
727 }
728
729 int64 CWalletDB::GetAccountCreditDebit(const string& strAccount)
730 {
731     list<CAccountingEntry> entries;
732     ListAccountCreditDebit(strAccount, entries);
733
734     int64 nCreditDebit = 0;
735     BOOST_FOREACH (const CAccountingEntry& entry, entries)
736         nCreditDebit += entry.nCreditDebit;
737
738     return nCreditDebit;
739 }
740
741 void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountingEntry>& entries)
742 {
743     bool fAllAccounts = (strAccount == "*");
744
745     Dbc* pcursor = GetCursor();
746     if (!pcursor)
747         throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor");
748     unsigned int fFlags = DB_SET_RANGE;
749     loop
750     {
751         // Read next record
752         CDataStream ssKey;
753         if (fFlags == DB_SET_RANGE)
754             ssKey << boost::make_tuple(string("acentry"), (fAllAccounts? string("") : strAccount), uint64(0));
755         CDataStream ssValue;
756         int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
757         fFlags = DB_NEXT;
758         if (ret == DB_NOTFOUND)
759             break;
760         else if (ret != 0)
761         {
762             pcursor->close();
763             throw runtime_error("CWalletDB::ListAccountCreditDebit() : error scanning DB");
764         }
765
766         // Unserialize
767         string strType;
768         ssKey >> strType;
769         if (strType != "acentry")
770             break;
771         CAccountingEntry acentry;
772         ssKey >> acentry.strAccount;
773         if (!fAllAccounts && acentry.strAccount != strAccount)
774             break;
775
776         ssValue >> acentry;
777         entries.push_back(acentry);
778     }
779
780     pcursor->close();
781 }
782
783
784 int CWalletDB::LoadWallet(CWallet* pwallet)
785 {
786     pwallet->vchDefaultKey.clear();
787     int nFileVersion = 0;
788     vector<uint256> vWalletUpgrade;
789     bool fIsEncrypted = false;
790
791     // Modify defaults
792 #ifndef WIN32
793     // Tray icon sometimes disappears on 9.10 karmic koala 64-bit, leaving no way to access the program
794     fMinimizeToTray = false;
795     fMinimizeOnClose = false;
796 #endif
797
798     //// todo: shouldn't we catch exceptions and try to recover and continue?
799     CRITICAL_BLOCK(pwallet->cs_wallet)
800     {
801         // Get cursor
802         Dbc* pcursor = GetCursor();
803         if (!pcursor)
804             return DB_CORRUPT;
805
806         loop
807         {
808             // Read next record
809             CDataStream ssKey;
810             CDataStream ssValue;
811             int ret = ReadAtCursor(pcursor, ssKey, ssValue);
812             if (ret == DB_NOTFOUND)
813                 break;
814             else if (ret != 0)
815                 return DB_CORRUPT;
816
817             // Unserialize
818             // Taking advantage of the fact that pair serialization
819             // is just the two items serialized one after the other
820             string strType;
821             ssKey >> strType;
822             if (strType == "name")
823             {
824                 string strAddress;
825                 ssKey >> strAddress;
826                 ssValue >> pwallet->mapAddressBook[strAddress];
827             }
828             else if (strType == "tx")
829             {
830                 uint256 hash;
831                 ssKey >> hash;
832                 CWalletTx& wtx = pwallet->mapWallet[hash];
833                 ssValue >> wtx;
834                 wtx.pwallet = pwallet;
835
836                 if (wtx.GetHash() != hash)
837                     printf("Error in wallet.dat, hash mismatch\n");
838
839                 // Undo serialize changes in 31600
840                 if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
841                 {
842                     if (!ssValue.empty())
843                     {
844                         char fTmp;
845                         char fUnused;
846                         ssValue >> fTmp >> fUnused >> wtx.strFromAccount;
847                         printf("LoadWallet() upgrading tx ver=%d %d '%s' %s\n", wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str());
848                         wtx.fTimeReceivedIsTxTime = fTmp;
849                     }
850                     else
851                     {
852                         printf("LoadWallet() repairing tx ver=%d %s\n", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str());
853                         wtx.fTimeReceivedIsTxTime = 0;
854                     }
855                     vWalletUpgrade.push_back(hash);
856                 }
857
858                 //// debug print
859                 //printf("LoadWallet  %s\n", wtx.GetHash().ToString().c_str());
860                 //printf(" %12I64d  %s  %s  %s\n",
861                 //    wtx.vout[0].nValue,
862                 //    DateTimeStrFormat("%x %H:%M:%S", wtx.GetBlockTime()).c_str(),
863                 //    wtx.hashBlock.ToString().substr(0,20).c_str(),
864                 //    wtx.mapValue["message"].c_str());
865             }
866             else if (strType == "acentry")
867             {
868                 string strAccount;
869                 ssKey >> strAccount;
870                 uint64 nNumber;
871                 ssKey >> nNumber;
872                 if (nNumber > nAccountingEntryNumber)
873                     nAccountingEntryNumber = nNumber;
874             }
875             else if (strType == "key" || strType == "wkey")
876             {
877                 vector<unsigned char> vchPubKey;
878                 ssKey >> vchPubKey;
879                 CKey key;
880                 if (strType == "key")
881                 {
882                     CPrivKey pkey;
883                     ssValue >> pkey;
884                     key.SetPrivKey(pkey);
885                 }
886                 else
887                 {
888                     CWalletKey wkey;
889                     ssValue >> wkey;
890                     key.SetPrivKey(wkey.vchPrivKey);
891                 }
892                 if (!pwallet->LoadKey(key))
893                     return DB_CORRUPT;
894             }
895             else if (strType == "mkey")
896             {
897                 unsigned int nID;
898                 ssKey >> nID;
899                 CMasterKey kMasterKey;
900                 ssValue >> kMasterKey;
901                 if(pwallet->mapMasterKeys.count(nID) != 0)
902                     return DB_CORRUPT;
903                 pwallet->mapMasterKeys[nID] = kMasterKey;
904                 if (pwallet->nMasterKeyMaxID < nID)
905                     pwallet->nMasterKeyMaxID = nID;
906             }
907             else if (strType == "ckey")
908             {
909                 vector<unsigned char> vchPubKey;
910                 ssKey >> vchPubKey;
911                 vector<unsigned char> vchPrivKey;
912                 ssValue >> vchPrivKey;
913                 if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey))
914                     return DB_CORRUPT;
915                 fIsEncrypted = true;
916             }
917             else if (strType == "defaultkey")
918             {
919                 ssValue >> pwallet->vchDefaultKey;
920             }
921             else if (strType == "pool")
922             {
923                 int64 nIndex;
924                 ssKey >> nIndex;
925                 pwallet->setKeyPool.insert(nIndex);
926             }
927             else if (strType == "version")
928             {
929                 ssValue >> nFileVersion;
930                 if (nFileVersion == 10300)
931                     nFileVersion = 300;
932             }
933             else if (strType == "setting")
934             {
935                 string strKey;
936                 ssKey >> strKey;
937
938                 // Options
939 #ifndef QT_GUI
940                 if (strKey == "fGenerateBitcoins")  ssValue >> fGenerateBitcoins;
941 #endif
942                 if (strKey == "nTransactionFee")    ssValue >> nTransactionFee;
943                 if (strKey == "fLimitProcessors")   ssValue >> fLimitProcessors;
944                 if (strKey == "nLimitProcessors")   ssValue >> nLimitProcessors;
945                 if (strKey == "fMinimizeToTray")    ssValue >> fMinimizeToTray;
946                 if (strKey == "fMinimizeOnClose")   ssValue >> fMinimizeOnClose;
947                 if (strKey == "fUseProxy")          ssValue >> fUseProxy;
948                 if (strKey == "addrProxy")          ssValue >> addrProxy;
949                 if (fHaveUPnP && strKey == "fUseUPnP")           ssValue >> fUseUPnP;
950             }
951             else if (strType == "minversion")
952             {
953                 int nMinVersion = 0;
954                 ssValue >> nMinVersion;
955                 if (nMinVersion > VERSION)
956                     return DB_TOO_NEW;
957             }
958         }
959         pcursor->close();
960     }
961
962     BOOST_FOREACH(uint256 hash, vWalletUpgrade)
963         WriteTx(hash, pwallet->mapWallet[hash]);
964
965     printf("nFileVersion = %d\n", nFileVersion);
966     printf("fGenerateBitcoins = %d\n", fGenerateBitcoins);
967     printf("nTransactionFee = %"PRI64d"\n", nTransactionFee);
968     printf("fMinimizeToTray = %d\n", fMinimizeToTray);
969     printf("fMinimizeOnClose = %d\n", fMinimizeOnClose);
970     printf("fUseProxy = %d\n", fUseProxy);
971     printf("addrProxy = %s\n", addrProxy.ToString().c_str());
972     if (fHaveUPnP)
973         printf("fUseUPnP = %d\n", fUseUPnP);
974
975
976     // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
977     if (fIsEncrypted && (nFileVersion == 40000 || nFileVersion == 50000))
978         return DB_NEED_REWRITE;
979
980     if (nFileVersion < VERSION) // Update
981     {
982         // Get rid of old debug.log file in current directory
983         if (nFileVersion <= 105 && !pszSetDataDir[0])
984             unlink("debug.log");
985
986         WriteVersion(VERSION);
987     }
988
989     return DB_LOAD_OK;
990 }
991
992 void ThreadFlushWalletDB(void* parg)
993 {
994     const string& strFile = ((const string*)parg)[0];
995     static bool fOneThread;
996     if (fOneThread)
997         return;
998     fOneThread = true;
999     if (mapArgs.count("-noflushwallet"))
1000         return;
1001
1002     unsigned int nLastSeen = nWalletDBUpdated;
1003     unsigned int nLastFlushed = nWalletDBUpdated;
1004     int64 nLastWalletUpdate = GetTime();
1005     while (!fShutdown)
1006     {
1007         Sleep(500);
1008
1009         if (nLastSeen != nWalletDBUpdated)
1010         {
1011             nLastSeen = nWalletDBUpdated;
1012             nLastWalletUpdate = GetTime();
1013         }
1014
1015         if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2)
1016         {
1017             TRY_CRITICAL_BLOCK(cs_db)
1018             {
1019                 // Don't do this if any databases are in use
1020                 int nRefCount = 0;
1021                 map<string, int>::iterator mi = mapFileUseCount.begin();
1022                 while (mi != mapFileUseCount.end())
1023                 {
1024                     nRefCount += (*mi).second;
1025                     mi++;
1026                 }
1027
1028                 if (nRefCount == 0 && !fShutdown)
1029                 {
1030                     map<string, int>::iterator mi = mapFileUseCount.find(strFile);
1031                     if (mi != mapFileUseCount.end())
1032                     {
1033                         printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str());
1034                         printf("Flushing wallet.dat\n");
1035                         nLastFlushed = nWalletDBUpdated;
1036                         int64 nStart = GetTimeMillis();
1037
1038                         // Flush wallet.dat so it's self contained
1039                         CloseDb(strFile);
1040                         dbenv.txn_checkpoint(0, 0, 0);
1041                         dbenv.lsn_reset(strFile.c_str(), 0);
1042
1043                         mapFileUseCount.erase(mi++);
1044                         printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart);
1045                     }
1046                 }
1047             }
1048         }
1049     }
1050 }
1051
1052 bool BackupWallet(const CWallet& wallet, const string& strDest)
1053 {
1054     if (!wallet.fFileBacked)
1055         return false;
1056     while (!fShutdown)
1057     {
1058         CRITICAL_BLOCK(cs_db)
1059         {
1060             if (!mapFileUseCount.count(wallet.strWalletFile) || mapFileUseCount[wallet.strWalletFile] == 0)
1061             {
1062                 // Flush log data to the dat file
1063                 CloseDb(wallet.strWalletFile);
1064                 dbenv.txn_checkpoint(0, 0, 0);
1065                 dbenv.lsn_reset(wallet.strWalletFile.c_str(), 0);
1066                 mapFileUseCount.erase(wallet.strWalletFile);
1067
1068                 // Copy wallet.dat
1069                 filesystem::path pathSrc(GetDataDir() + "/" + wallet.strWalletFile);
1070                 filesystem::path pathDest(strDest);
1071                 if (filesystem::is_directory(pathDest))
1072                     pathDest = pathDest / wallet.strWalletFile;
1073 #if BOOST_VERSION >= 104000
1074                 filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists);
1075 #else
1076                 filesystem::copy_file(pathSrc, pathDest);
1077 #endif
1078                 printf("copied wallet.dat to %s\n", pathDest.string().c_str());
1079
1080                 return true;
1081             }
1082         }
1083         Sleep(100);
1084     }
1085     return false;
1086 }