update fSpent flag on wallet transactions if they're seen spent in case copy of walle...
[novacoin.git] / db.cpp
1 // Copyright (c) 2009-2010 Satoshi Nakamoto\r
2 // Distributed under the MIT/X11 software license, see the accompanying\r
3 // file license.txt or http://www.opensource.org/licenses/mit-license.php.\r
4 \r
5 #include "headers.h"\r
6 \r
7 void ThreadFlushWalletDB(void* parg);\r
8 \r
9 \r
10 unsigned int nWalletDBUpdated;\r
11 \r
12 \r
13 \r
14 \r
15 //\r
16 // CDB\r
17 //\r
18 \r
19 static CCriticalSection cs_db;\r
20 static bool fDbEnvInit = false;\r
21 DbEnv dbenv(0);\r
22 static map<string, int> mapFileUseCount;\r
23 static map<string, Db*> mapDb;\r
24 \r
25 class CDBInit\r
26 {\r
27 public:\r
28     CDBInit()\r
29     {\r
30     }\r
31     ~CDBInit()\r
32     {\r
33         if (fDbEnvInit)\r
34         {\r
35             dbenv.close(0);\r
36             fDbEnvInit = false;\r
37         }\r
38     }\r
39 }\r
40 instance_of_cdbinit;\r
41 \r
42 \r
43 CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL)\r
44 {\r
45     int ret;\r
46     if (pszFile == NULL)\r
47         return;\r
48 \r
49     fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));\r
50     bool fCreate = strchr(pszMode, 'c');\r
51     unsigned int nFlags = DB_THREAD;\r
52     if (fCreate)\r
53         nFlags |= DB_CREATE;\r
54 \r
55     CRITICAL_BLOCK(cs_db)\r
56     {\r
57         if (!fDbEnvInit)\r
58         {\r
59             if (fShutdown)\r
60                 return;\r
61             string strDataDir = GetDataDir();\r
62             string strLogDir = strDataDir + "/database";\r
63             _mkdir(strLogDir.c_str());\r
64             string strErrorFile = strDataDir + "/db.log";\r
65             printf("dbenv.open strLogDir=%s strErrorFile=%s\n", strLogDir.c_str(), strErrorFile.c_str());\r
66 \r
67             dbenv.set_lg_dir(strLogDir.c_str());\r
68             dbenv.set_lg_max(10000000);\r
69             dbenv.set_lk_max_locks(10000);\r
70             dbenv.set_lk_max_objects(10000);\r
71             dbenv.set_errfile(fopen(strErrorFile.c_str(), "a")); /// debug\r
72             dbenv.set_flags(DB_AUTO_COMMIT, 1);\r
73             ret = dbenv.open(strDataDir.c_str(),\r
74                              DB_CREATE     |\r
75                              DB_INIT_LOCK  |\r
76                              DB_INIT_LOG   |\r
77                              DB_INIT_MPOOL |\r
78                              DB_INIT_TXN   |\r
79                              DB_THREAD     |\r
80                              DB_PRIVATE    |\r
81                              DB_RECOVER,\r
82                              S_IRUSR | S_IWUSR);\r
83             if (ret > 0)\r
84                 throw runtime_error(strprintf("CDB() : error %d opening database environment\n", ret));\r
85             fDbEnvInit = true;\r
86         }\r
87 \r
88         strFile = pszFile;\r
89         ++mapFileUseCount[strFile];\r
90         pdb = mapDb[strFile];\r
91         if (pdb == NULL)\r
92         {\r
93             pdb = new Db(&dbenv, 0);\r
94 \r
95             ret = pdb->open(NULL,      // Txn pointer\r
96                             pszFile,   // Filename\r
97                             "main",    // Logical db name\r
98                             DB_BTREE,  // Database type\r
99                             nFlags,    // Flags\r
100                             0);\r
101 \r
102             if (ret > 0)\r
103             {\r
104                 delete pdb;\r
105                 pdb = NULL;\r
106                 CRITICAL_BLOCK(cs_db)\r
107                     --mapFileUseCount[strFile];\r
108                 strFile = "";\r
109                 throw runtime_error(strprintf("CDB() : can't open database file %s, error %d\n", pszFile, ret));\r
110             }\r
111 \r
112             if (fCreate && !Exists(string("version")))\r
113             {\r
114                 bool fTmp = fReadOnly;\r
115                 fReadOnly = false;\r
116                 WriteVersion(VERSION);\r
117                 fReadOnly = fTmp;\r
118             }\r
119 \r
120             mapDb[strFile] = pdb;\r
121         }\r
122     }\r
123 }\r
124 \r
125 void CDB::Close()\r
126 {\r
127     if (!pdb)\r
128         return;\r
129     if (!vTxn.empty())\r
130         vTxn.front()->abort();\r
131     vTxn.clear();\r
132     pdb = NULL;\r
133     dbenv.txn_checkpoint(0, 0, 0);\r
134 \r
135     CRITICAL_BLOCK(cs_db)\r
136         --mapFileUseCount[strFile];\r
137 }\r
138 \r
139 void CloseDb(const string& strFile)\r
140 {\r
141     CRITICAL_BLOCK(cs_db)\r
142     {\r
143         if (mapDb[strFile] != NULL)\r
144         {\r
145             // Close the database handle\r
146             Db* pdb = mapDb[strFile];\r
147             pdb->close(0);\r
148             delete pdb;\r
149             mapDb[strFile] = NULL;\r
150         }\r
151     }\r
152 }\r
153 \r
154 void DBFlush(bool fShutdown)\r
155 {\r
156     // Flush log data to the actual data file\r
157     //  on all files that are not in use\r
158     printf("DBFlush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started");\r
159     if (!fDbEnvInit)\r
160         return;\r
161     CRITICAL_BLOCK(cs_db)\r
162     {\r
163         map<string, int>::iterator mi = mapFileUseCount.begin();\r
164         while (mi != mapFileUseCount.end())\r
165         {\r
166             string strFile = (*mi).first;\r
167             int nRefCount = (*mi).second;\r
168             printf("%s refcount=%d\n", strFile.c_str(), nRefCount);\r
169             if (nRefCount == 0)\r
170             {\r
171                 // Move log data to the dat file\r
172                 CloseDb(strFile);\r
173                 dbenv.txn_checkpoint(0, 0, 0);\r
174                 printf("%s flush\n", strFile.c_str());\r
175                 dbenv.lsn_reset(strFile.c_str(), 0);\r
176                 mapFileUseCount.erase(mi++);\r
177             }\r
178             else\r
179                 mi++;\r
180         }\r
181         if (fShutdown)\r
182         {\r
183             char** listp;\r
184             if (mapFileUseCount.empty())\r
185                 dbenv.log_archive(&listp, DB_ARCH_REMOVE);\r
186             dbenv.close(0);\r
187             fDbEnvInit = false;\r
188         }\r
189     }\r
190 }\r
191 \r
192 \r
193 \r
194 \r
195 \r
196 \r
197 //\r
198 // CTxDB\r
199 //\r
200 \r
201 bool CTxDB::ReadTxIndex(uint256 hash, CTxIndex& txindex)\r
202 {\r
203     assert(!fClient);\r
204     txindex.SetNull();\r
205     return Read(make_pair(string("tx"), hash), txindex);\r
206 }\r
207 \r
208 bool CTxDB::UpdateTxIndex(uint256 hash, const CTxIndex& txindex)\r
209 {\r
210     assert(!fClient);\r
211     return Write(make_pair(string("tx"), hash), txindex);\r
212 }\r
213 \r
214 bool CTxDB::AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight)\r
215 {\r
216     assert(!fClient);\r
217 \r
218     // Add to tx index\r
219     uint256 hash = tx.GetHash();\r
220     CTxIndex txindex(pos, tx.vout.size());\r
221     return Write(make_pair(string("tx"), hash), txindex);\r
222 }\r
223 \r
224 bool CTxDB::EraseTxIndex(const CTransaction& tx)\r
225 {\r
226     assert(!fClient);\r
227     uint256 hash = tx.GetHash();\r
228 \r
229     return Erase(make_pair(string("tx"), hash));\r
230 }\r
231 \r
232 bool CTxDB::ContainsTx(uint256 hash)\r
233 {\r
234     assert(!fClient);\r
235     return Exists(make_pair(string("tx"), hash));\r
236 }\r
237 \r
238 bool CTxDB::ReadOwnerTxes(uint160 hash160, int nMinHeight, vector<CTransaction>& vtx)\r
239 {\r
240     assert(!fClient);\r
241     vtx.clear();\r
242 \r
243     // Get cursor\r
244     Dbc* pcursor = GetCursor();\r
245     if (!pcursor)\r
246         return false;\r
247 \r
248     unsigned int fFlags = DB_SET_RANGE;\r
249     loop\r
250     {\r
251         // Read next record\r
252         CDataStream ssKey;\r
253         if (fFlags == DB_SET_RANGE)\r
254             ssKey << string("owner") << hash160 << CDiskTxPos(0, 0, 0);\r
255         CDataStream ssValue;\r
256         int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);\r
257         fFlags = DB_NEXT;\r
258         if (ret == DB_NOTFOUND)\r
259             break;\r
260         else if (ret != 0)\r
261         {\r
262             pcursor->close();\r
263             return false;\r
264         }\r
265 \r
266         // Unserialize\r
267         string strType;\r
268         uint160 hashItem;\r
269         CDiskTxPos pos;\r
270         ssKey >> strType >> hashItem >> pos;\r
271         int nItemHeight;\r
272         ssValue >> nItemHeight;\r
273 \r
274         // Read transaction\r
275         if (strType != "owner" || hashItem != hash160)\r
276             break;\r
277         if (nItemHeight >= nMinHeight)\r
278         {\r
279             vtx.resize(vtx.size()+1);\r
280             if (!vtx.back().ReadFromDisk(pos))\r
281             {\r
282                 pcursor->close();\r
283                 return false;\r
284             }\r
285         }\r
286     }\r
287 \r
288     pcursor->close();\r
289     return true;\r
290 }\r
291 \r
292 bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex)\r
293 {\r
294     assert(!fClient);\r
295     tx.SetNull();\r
296     if (!ReadTxIndex(hash, txindex))\r
297         return false;\r
298     return (tx.ReadFromDisk(txindex.pos));\r
299 }\r
300 \r
301 bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx)\r
302 {\r
303     CTxIndex txindex;\r
304     return ReadDiskTx(hash, tx, txindex);\r
305 }\r
306 \r
307 bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex)\r
308 {\r
309     return ReadDiskTx(outpoint.hash, tx, txindex);\r
310 }\r
311 \r
312 bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx)\r
313 {\r
314     CTxIndex txindex;\r
315     return ReadDiskTx(outpoint.hash, tx, txindex);\r
316 }\r
317 \r
318 bool CTxDB::WriteBlockIndex(const CDiskBlockIndex& blockindex)\r
319 {\r
320     return Write(make_pair(string("blockindex"), blockindex.GetBlockHash()), blockindex);\r
321 }\r
322 \r
323 bool CTxDB::EraseBlockIndex(uint256 hash)\r
324 {\r
325     return Erase(make_pair(string("blockindex"), hash));\r
326 }\r
327 \r
328 bool CTxDB::ReadHashBestChain(uint256& hashBestChain)\r
329 {\r
330     return Read(string("hashBestChain"), hashBestChain);\r
331 }\r
332 \r
333 bool CTxDB::WriteHashBestChain(uint256 hashBestChain)\r
334 {\r
335     return Write(string("hashBestChain"), hashBestChain);\r
336 }\r
337 \r
338 CBlockIndex* InsertBlockIndex(uint256 hash)\r
339 {\r
340     if (hash == 0)\r
341         return NULL;\r
342 \r
343     // Return existing\r
344     map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash);\r
345     if (mi != mapBlockIndex.end())\r
346         return (*mi).second;\r
347 \r
348     // Create new\r
349     CBlockIndex* pindexNew = new CBlockIndex();\r
350     if (!pindexNew)\r
351         throw runtime_error("LoadBlockIndex() : new CBlockIndex failed");\r
352     mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;\r
353     pindexNew->phashBlock = &((*mi).first);\r
354 \r
355     return pindexNew;\r
356 }\r
357 \r
358 bool CTxDB::LoadBlockIndex()\r
359 {\r
360     // Get cursor\r
361     Dbc* pcursor = GetCursor();\r
362     if (!pcursor)\r
363         return false;\r
364 \r
365     unsigned int fFlags = DB_SET_RANGE;\r
366     loop\r
367     {\r
368         // Read next record\r
369         CDataStream ssKey;\r
370         if (fFlags == DB_SET_RANGE)\r
371             ssKey << make_pair(string("blockindex"), uint256(0));\r
372         CDataStream ssValue;\r
373         int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);\r
374         fFlags = DB_NEXT;\r
375         if (ret == DB_NOTFOUND)\r
376             break;\r
377         else if (ret != 0)\r
378             return false;\r
379 \r
380         // Unserialize\r
381         string strType;\r
382         ssKey >> strType;\r
383         if (strType == "blockindex")\r
384         {\r
385             CDiskBlockIndex diskindex;\r
386             ssValue >> diskindex;\r
387 \r
388             // Construct block index object\r
389             CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash());\r
390             pindexNew->pprev          = InsertBlockIndex(diskindex.hashPrev);\r
391             pindexNew->pnext          = InsertBlockIndex(diskindex.hashNext);\r
392             pindexNew->nFile          = diskindex.nFile;\r
393             pindexNew->nBlockPos      = diskindex.nBlockPos;\r
394             pindexNew->nHeight        = diskindex.nHeight;\r
395             pindexNew->nVersion       = diskindex.nVersion;\r
396             pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;\r
397             pindexNew->nTime          = diskindex.nTime;\r
398             pindexNew->nBits          = diskindex.nBits;\r
399             pindexNew->nNonce         = diskindex.nNonce;\r
400 \r
401             // Watch for genesis block and best block\r
402             if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == hashGenesisBlock)\r
403                 pindexGenesisBlock = pindexNew;\r
404         }\r
405         else\r
406         {\r
407             break;\r
408         }\r
409     }\r
410     pcursor->close();\r
411 \r
412     if (!ReadHashBestChain(hashBestChain))\r
413     {\r
414         if (pindexGenesisBlock == NULL)\r
415             return true;\r
416         return error("CTxDB::LoadBlockIndex() : hashBestChain not found");\r
417     }\r
418 \r
419     if (!mapBlockIndex.count(hashBestChain))\r
420         return error("CTxDB::LoadBlockIndex() : blockindex for hashBestChain not found");\r
421     pindexBest = mapBlockIndex[hashBestChain];\r
422     nBestHeight = pindexBest->nHeight;\r
423     printf("LoadBlockIndex(): hashBestChain=%s  height=%d\n", hashBestChain.ToString().substr(0,16).c_str(), nBestHeight);\r
424 \r
425     return true;\r
426 }\r
427 \r
428 \r
429 \r
430 \r
431 \r
432 //\r
433 // CAddrDB\r
434 //\r
435 \r
436 bool CAddrDB::WriteAddress(const CAddress& addr)\r
437 {\r
438     return Write(make_pair(string("addr"), addr.GetKey()), addr);\r
439 }\r
440 \r
441 bool CAddrDB::LoadAddresses()\r
442 {\r
443     CRITICAL_BLOCK(cs_mapAddresses)\r
444     {\r
445         // Load user provided addresses\r
446         CAutoFile filein = fopen((GetDataDir() + "/addr.txt").c_str(), "rt");\r
447         if (filein)\r
448         {\r
449             try\r
450             {\r
451                 char psz[1000];\r
452                 while (fgets(psz, sizeof(psz), filein))\r
453                 {\r
454                     CAddress addr(psz, NODE_NETWORK);\r
455                     addr.nTime = 0; // so it won't relay unless successfully connected\r
456                     if (addr.IsValid())\r
457                         AddAddress(addr);\r
458                 }\r
459             }\r
460             catch (...) { }\r
461         }\r
462 \r
463         // Get cursor\r
464         Dbc* pcursor = GetCursor();\r
465         if (!pcursor)\r
466             return false;\r
467 \r
468         loop\r
469         {\r
470             // Read next record\r
471             CDataStream ssKey;\r
472             CDataStream ssValue;\r
473             int ret = ReadAtCursor(pcursor, ssKey, ssValue);\r
474             if (ret == DB_NOTFOUND)\r
475                 break;\r
476             else if (ret != 0)\r
477                 return false;\r
478 \r
479             // Unserialize\r
480             string strType;\r
481             ssKey >> strType;\r
482             if (strType == "addr")\r
483             {\r
484                 CAddress addr;\r
485                 ssValue >> addr;\r
486                 mapAddresses.insert(make_pair(addr.GetKey(), addr));\r
487             }\r
488         }\r
489         pcursor->close();\r
490 \r
491         printf("Loaded %d addresses\n", mapAddresses.size());\r
492 \r
493         // Fix for possible bug that manifests in mapAddresses.count in irc.cpp,\r
494         // just need to call count here and it doesn't happen there.  The bug was the\r
495         // pack pragma in irc.cpp and has been fixed, but I'm not in a hurry to delete this.\r
496         mapAddresses.count(vector<unsigned char>(18));\r
497     }\r
498 \r
499     return true;\r
500 }\r
501 \r
502 bool LoadAddresses()\r
503 {\r
504     return CAddrDB("cr+").LoadAddresses();\r
505 }\r
506 \r
507 \r
508 \r
509 \r
510 //\r
511 // CReviewDB\r
512 //\r
513 \r
514 bool CReviewDB::ReadReviews(uint256 hash, vector<CReview>& vReviews)\r
515 {\r
516     vReviews.size(); // msvc workaround, just need to do anything with vReviews\r
517     return Read(make_pair(string("reviews"), hash), vReviews);\r
518 }\r
519 \r
520 bool CReviewDB::WriteReviews(uint256 hash, const vector<CReview>& vReviews)\r
521 {\r
522     return Write(make_pair(string("reviews"), hash), vReviews);\r
523 }\r
524 \r
525 \r
526 \r
527 \r
528 \r
529 \r
530 \r
531 //\r
532 // CWalletDB\r
533 //\r
534 \r
535 bool CWalletDB::LoadWallet(vector<unsigned char>& vchDefaultKeyRet)\r
536 {\r
537     vchDefaultKeyRet.clear();\r
538     int nFileVersion = 0;\r
539 \r
540     // Modify defaults\r
541 #ifndef __WXMSW__\r
542     // Tray icon sometimes disappears on 9.10 karmic koala 64-bit, leaving no way to access the program\r
543     fMinimizeToTray = false;\r
544     fMinimizeOnClose = false;\r
545 #endif\r
546 \r
547     //// todo: shouldn't we catch exceptions and try to recover and continue?\r
548     CRITICAL_BLOCK(cs_mapKeys)\r
549     CRITICAL_BLOCK(cs_mapWallet)\r
550     {\r
551         // Get cursor\r
552         Dbc* pcursor = GetCursor();\r
553         if (!pcursor)\r
554             return false;\r
555 \r
556         loop\r
557         {\r
558             // Read next record\r
559             CDataStream ssKey;\r
560             CDataStream ssValue;\r
561             int ret = ReadAtCursor(pcursor, ssKey, ssValue);\r
562             if (ret == DB_NOTFOUND)\r
563                 break;\r
564             else if (ret != 0)\r
565                 return false;\r
566 \r
567             // Unserialize\r
568             // Taking advantage of the fact that pair serialization\r
569             // is just the two items serialized one after the other\r
570             string strType;\r
571             ssKey >> strType;\r
572             if (strType == "name")\r
573             {\r
574                 string strAddress;\r
575                 ssKey >> strAddress;\r
576                 ssValue >> mapAddressBook[strAddress];\r
577             }\r
578             else if (strType == "tx")\r
579             {\r
580                 uint256 hash;\r
581                 ssKey >> hash;\r
582                 CWalletTx& wtx = mapWallet[hash];\r
583                 ssValue >> wtx;\r
584 \r
585                 if (wtx.GetHash() != hash)\r
586                     printf("Error in wallet.dat, hash mismatch\n");\r
587 \r
588                 //// debug print\r
589                 //printf("LoadWallet  %s\n", wtx.GetHash().ToString().c_str());\r
590                 //printf(" %12I64d  %s  %s  %s\n",\r
591                 //    wtx.vout[0].nValue,\r
592                 //    DateTimeStrFormat("%x %H:%M:%S", wtx.nTime).c_str(),\r
593                 //    wtx.hashBlock.ToString().substr(0,16).c_str(),\r
594                 //    wtx.mapValue["message"].c_str());\r
595             }\r
596             else if (strType == "key" || strType == "wkey")\r
597             {\r
598                 vector<unsigned char> vchPubKey;\r
599                 ssKey >> vchPubKey;\r
600                 CWalletKey wkey;\r
601                 if (strType == "key")\r
602                     ssValue >> wkey.vchPrivKey;\r
603                 else\r
604                     ssValue >> wkey;\r
605 \r
606                 mapKeys[vchPubKey] = wkey.vchPrivKey;\r
607                 mapPubKeys[Hash160(vchPubKey)] = vchPubKey;\r
608             }\r
609             else if (strType == "defaultkey")\r
610             {\r
611                 ssValue >> vchDefaultKeyRet;\r
612             }\r
613             else if (strType == "version")\r
614             {\r
615                 ssValue >> nFileVersion;\r
616             }\r
617             else if (strType == "setting")\r
618             {\r
619                 string strKey;\r
620                 ssKey >> strKey;\r
621 \r
622                 // Menu state\r
623                 if (strKey == "fShowGenerated")     ssValue >> fShowGenerated;\r
624                 if (strKey == "fGenerateBitcoins")  ssValue >> fGenerateBitcoins;\r
625 \r
626                 // Options\r
627                 if (strKey == "nTransactionFee")    ssValue >> nTransactionFee;\r
628                 if (strKey == "addrIncoming")       ssValue >> addrIncoming;\r
629                 if (strKey == "fLimitProcessors")   ssValue >> fLimitProcessors;\r
630                 if (strKey == "nLimitProcessors")   ssValue >> nLimitProcessors;\r
631                 if (strKey == "fMinimizeToTray")    ssValue >> fMinimizeToTray;\r
632                 if (strKey == "fMinimizeOnClose")   ssValue >> fMinimizeOnClose;\r
633                 if (strKey == "fUseProxy")          ssValue >> fUseProxy;\r
634                 if (strKey == "addrProxy")          ssValue >> addrProxy;\r
635 \r
636             }\r
637         }\r
638         pcursor->close();\r
639     }\r
640 \r
641     printf("nFileVersion = %d\n", nFileVersion);\r
642     printf("fShowGenerated = %d\n", fShowGenerated);\r
643     printf("fGenerateBitcoins = %d\n", fGenerateBitcoins);\r
644     printf("nTransactionFee = %"PRI64d"\n", nTransactionFee);\r
645     printf("addrIncoming = %s\n", addrIncoming.ToString().c_str());\r
646     printf("fMinimizeToTray = %d\n", fMinimizeToTray);\r
647     printf("fMinimizeOnClose = %d\n", fMinimizeOnClose);\r
648     printf("fUseProxy = %d\n", fUseProxy);\r
649     printf("addrProxy = %s\n", addrProxy.ToString().c_str());\r
650 \r
651 \r
652     // The transaction fee setting won't be needed for many years to come.\r
653     // Setting it to zero here in case they set it to something in an earlier version.\r
654     if (nTransactionFee != 0)\r
655     {\r
656         nTransactionFee = 0;\r
657         WriteSetting("nTransactionFee", nTransactionFee);\r
658     }\r
659 \r
660     // Upgrade\r
661     if (nFileVersion < VERSION)\r
662     {\r
663         // Get rid of old debug.log file in current directory\r
664         if (nFileVersion <= 105 && !pszSetDataDir[0])\r
665             unlink("debug.log");\r
666 \r
667         WriteVersion(VERSION);\r
668     }\r
669 \r
670     return true;\r
671 }\r
672 \r
673 bool LoadWallet(bool& fFirstRunRet)\r
674 {\r
675     fFirstRunRet = false;\r
676     vector<unsigned char> vchDefaultKey;\r
677     if (!CWalletDB("cr+").LoadWallet(vchDefaultKey))\r
678         return false;\r
679     fFirstRunRet = vchDefaultKey.empty();\r
680 \r
681     if (mapKeys.count(vchDefaultKey))\r
682     {\r
683         // Set keyUser\r
684         keyUser.SetPubKey(vchDefaultKey);\r
685         keyUser.SetPrivKey(mapKeys[vchDefaultKey]);\r
686     }\r
687     else\r
688     {\r
689         // Create new keyUser and set as default key\r
690         RandAddSeedPerfmon();\r
691         keyUser.MakeNewKey();\r
692         if (!AddKey(keyUser))\r
693             return false;\r
694         if (!SetAddressBookName(PubKeyToAddress(keyUser.GetPubKey()), "Your Address"))\r
695             return false;\r
696         CWalletDB().WriteDefaultKey(keyUser.GetPubKey());\r
697     }\r
698 \r
699     CreateThread(ThreadFlushWalletDB, NULL);\r
700     return true;\r
701 }\r
702 \r
703 void ThreadFlushWalletDB(void* parg)\r
704 {\r
705     static bool fOneThread;\r
706     if (fOneThread)\r
707         return;\r
708     fOneThread = true;\r
709     if (mapArgs.count("-noflushwallet"))\r
710         return;\r
711 \r
712     unsigned int nLastSeen = nWalletDBUpdated;\r
713     unsigned int nLastFlushed = nWalletDBUpdated;\r
714     int64 nLastWalletUpdate = GetTime();\r
715     while (!fShutdown)\r
716     {\r
717         Sleep(500);\r
718 \r
719         if (nLastSeen != nWalletDBUpdated)\r
720         {\r
721             nLastSeen = nWalletDBUpdated;\r
722             nLastWalletUpdate = GetTime();\r
723         }\r
724 \r
725         if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2)\r
726         {\r
727             TRY_CRITICAL_BLOCK(cs_db)\r
728             {\r
729                 // Don't do this if any databases are in use\r
730                 int nRefCount = 0;\r
731                 map<string, int>::iterator mi = mapFileUseCount.begin();\r
732                 while (mi != mapFileUseCount.end())\r
733                 {\r
734                     nRefCount += (*mi).second;\r
735                     mi++;\r
736                 }\r
737 \r
738                 if (nRefCount == 0 && !fShutdown)\r
739                 {\r
740                     string strFile = "wallet.dat";\r
741                     map<string, int>::iterator mi = mapFileUseCount.find(strFile);\r
742                     if (mi != mapFileUseCount.end())\r
743                     {\r
744                         printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str());\r
745                         printf("Flushing wallet.dat\n");\r
746                         nLastFlushed = nWalletDBUpdated;\r
747                         int64 nStart = GetTimeMillis();\r
748 \r
749                         // Flush wallet.dat so it's self contained\r
750                         CloseDb(strFile);\r
751                         dbenv.txn_checkpoint(0, 0, 0);\r
752                         dbenv.lsn_reset(strFile.c_str(), 0);\r
753 \r
754                         mapFileUseCount.erase(mi++);\r
755                         printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart);\r
756                     }\r
757                 }\r
758             }\r
759         }\r
760     }\r
761 }\r