bugfix Db::open/close and zombie sockets bugs fix double-close of socket handle,
[novacoin.git] / db.cpp
1 // Copyright (c) 2009 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                              0);\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     RandAddSeed();\r
139 }\r
140 \r
141 void CloseDb(const string& strFile)\r
142 {\r
143     CRITICAL_BLOCK(cs_db)\r
144     {\r
145         if (mapDb[strFile] != NULL)\r
146         {\r
147             // Close the database handle\r
148             Db* pdb = mapDb[strFile];\r
149             pdb->close(0);\r
150             delete pdb;\r
151             mapDb[strFile] = NULL;\r
152         }\r
153     }\r
154 }\r
155 \r
156 void DBFlush(bool fShutdown)\r
157 {\r
158     // Flush log data to the actual data file\r
159     //  on all files that are not in use\r
160     printf("DBFlush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started");\r
161     if (!fDbEnvInit)\r
162         return;\r
163     CRITICAL_BLOCK(cs_db)\r
164     {\r
165         map<string, int>::iterator mi = mapFileUseCount.begin();\r
166         while (mi != mapFileUseCount.end())\r
167         {\r
168             string strFile = (*mi).first;\r
169             int nRefCount = (*mi).second;\r
170             printf("%s refcount=%d\n", strFile.c_str(), nRefCount);\r
171             if (nRefCount == 0)\r
172             {\r
173                 // Move log data to the dat file\r
174                 CloseDb(strFile);\r
175                 dbenv.txn_checkpoint(0, 0, 0);\r
176                 printf("%s flush\n", strFile.c_str());\r
177                 dbenv.lsn_reset(strFile.c_str(), 0);\r
178                 mapFileUseCount.erase(mi++);\r
179             }\r
180             else\r
181                 mi++;\r
182         }\r
183         if (fShutdown)\r
184         {\r
185             char** listp;\r
186             if (mapFileUseCount.empty())\r
187                 dbenv.log_archive(&listp, DB_ARCH_REMOVE);\r
188             dbenv.close(0);\r
189             fDbEnvInit = false;\r
190         }\r
191     }\r
192 }\r
193 \r
194 \r
195 \r
196 \r
197 \r
198 \r
199 //\r
200 // CTxDB\r
201 //\r
202 \r
203 bool CTxDB::ReadTxIndex(uint256 hash, CTxIndex& txindex)\r
204 {\r
205     assert(!fClient);\r
206     txindex.SetNull();\r
207     return Read(make_pair(string("tx"), hash), txindex);\r
208 }\r
209 \r
210 bool CTxDB::UpdateTxIndex(uint256 hash, const CTxIndex& txindex)\r
211 {\r
212     assert(!fClient);\r
213     return Write(make_pair(string("tx"), hash), txindex);\r
214 }\r
215 \r
216 bool CTxDB::AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight)\r
217 {\r
218     assert(!fClient);\r
219 \r
220     // Add to tx index\r
221     uint256 hash = tx.GetHash();\r
222     CTxIndex txindex(pos, tx.vout.size());\r
223     return Write(make_pair(string("tx"), hash), txindex);\r
224 }\r
225 \r
226 bool CTxDB::EraseTxIndex(const CTransaction& tx)\r
227 {\r
228     assert(!fClient);\r
229     uint256 hash = tx.GetHash();\r
230 \r
231     return Erase(make_pair(string("tx"), hash));\r
232 }\r
233 \r
234 bool CTxDB::ContainsTx(uint256 hash)\r
235 {\r
236     assert(!fClient);\r
237     return Exists(make_pair(string("tx"), hash));\r
238 }\r
239 \r
240 bool CTxDB::ReadOwnerTxes(uint160 hash160, int nMinHeight, vector<CTransaction>& vtx)\r
241 {\r
242     assert(!fClient);\r
243     vtx.clear();\r
244 \r
245     // Get cursor\r
246     Dbc* pcursor = GetCursor();\r
247     if (!pcursor)\r
248         return false;\r
249 \r
250     unsigned int fFlags = DB_SET_RANGE;\r
251     loop\r
252     {\r
253         // Read next record\r
254         CDataStream ssKey;\r
255         if (fFlags == DB_SET_RANGE)\r
256             ssKey << string("owner") << hash160 << CDiskTxPos(0, 0, 0);\r
257         CDataStream ssValue;\r
258         int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);\r
259         fFlags = DB_NEXT;\r
260         if (ret == DB_NOTFOUND)\r
261             break;\r
262         else if (ret != 0)\r
263         {\r
264             pcursor->close();\r
265             return false;\r
266         }\r
267 \r
268         // Unserialize\r
269         string strType;\r
270         uint160 hashItem;\r
271         CDiskTxPos pos;\r
272         ssKey >> strType >> hashItem >> pos;\r
273         int nItemHeight;\r
274         ssValue >> nItemHeight;\r
275 \r
276         // Read transaction\r
277         if (strType != "owner" || hashItem != hash160)\r
278             break;\r
279         if (nItemHeight >= nMinHeight)\r
280         {\r
281             vtx.resize(vtx.size()+1);\r
282             if (!vtx.back().ReadFromDisk(pos))\r
283             {\r
284                 pcursor->close();\r
285                 return false;\r
286             }\r
287         }\r
288     }\r
289 \r
290     pcursor->close();\r
291     return true;\r
292 }\r
293 \r
294 bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex)\r
295 {\r
296     assert(!fClient);\r
297     tx.SetNull();\r
298     if (!ReadTxIndex(hash, txindex))\r
299         return false;\r
300     return (tx.ReadFromDisk(txindex.pos));\r
301 }\r
302 \r
303 bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx)\r
304 {\r
305     CTxIndex txindex;\r
306     return ReadDiskTx(hash, tx, txindex);\r
307 }\r
308 \r
309 bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex)\r
310 {\r
311     return ReadDiskTx(outpoint.hash, tx, txindex);\r
312 }\r
313 \r
314 bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx)\r
315 {\r
316     CTxIndex txindex;\r
317     return ReadDiskTx(outpoint.hash, tx, txindex);\r
318 }\r
319 \r
320 bool CTxDB::WriteBlockIndex(const CDiskBlockIndex& blockindex)\r
321 {\r
322     return Write(make_pair(string("blockindex"), blockindex.GetBlockHash()), blockindex);\r
323 }\r
324 \r
325 bool CTxDB::EraseBlockIndex(uint256 hash)\r
326 {\r
327     return Erase(make_pair(string("blockindex"), hash));\r
328 }\r
329 \r
330 bool CTxDB::ReadHashBestChain(uint256& hashBestChain)\r
331 {\r
332     return Read(string("hashBestChain"), hashBestChain);\r
333 }\r
334 \r
335 bool CTxDB::WriteHashBestChain(uint256 hashBestChain)\r
336 {\r
337     return Write(string("hashBestChain"), hashBestChain);\r
338 }\r
339 \r
340 CBlockIndex* InsertBlockIndex(uint256 hash)\r
341 {\r
342     if (hash == 0)\r
343         return NULL;\r
344 \r
345     // Return existing\r
346     map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash);\r
347     if (mi != mapBlockIndex.end())\r
348         return (*mi).second;\r
349 \r
350     // Create new\r
351     CBlockIndex* pindexNew = new CBlockIndex();\r
352     if (!pindexNew)\r
353         throw runtime_error("LoadBlockIndex() : new CBlockIndex failed");\r
354     mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;\r
355     pindexNew->phashBlock = &((*mi).first);\r
356 \r
357     return pindexNew;\r
358 }\r
359 \r
360 bool CTxDB::LoadBlockIndex()\r
361 {\r
362     // Get cursor\r
363     Dbc* pcursor = GetCursor();\r
364     if (!pcursor)\r
365         return false;\r
366 \r
367     unsigned int fFlags = DB_SET_RANGE;\r
368     loop\r
369     {\r
370         // Read next record\r
371         CDataStream ssKey;\r
372         if (fFlags == DB_SET_RANGE)\r
373             ssKey << make_pair(string("blockindex"), uint256(0));\r
374         CDataStream ssValue;\r
375         int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);\r
376         fFlags = DB_NEXT;\r
377         if (ret == DB_NOTFOUND)\r
378             break;\r
379         else if (ret != 0)\r
380             return false;\r
381 \r
382         // Unserialize\r
383         string strType;\r
384         ssKey >> strType;\r
385         if (strType == "blockindex")\r
386         {\r
387             CDiskBlockIndex diskindex;\r
388             ssValue >> diskindex;\r
389 \r
390             // Construct block index object\r
391             CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash());\r
392             pindexNew->pprev          = InsertBlockIndex(diskindex.hashPrev);\r
393             pindexNew->pnext          = InsertBlockIndex(diskindex.hashNext);\r
394             pindexNew->nFile          = diskindex.nFile;\r
395             pindexNew->nBlockPos      = diskindex.nBlockPos;\r
396             pindexNew->nHeight        = diskindex.nHeight;\r
397             pindexNew->nVersion       = diskindex.nVersion;\r
398             pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;\r
399             pindexNew->nTime          = diskindex.nTime;\r
400             pindexNew->nBits          = diskindex.nBits;\r
401             pindexNew->nNonce         = diskindex.nNonce;\r
402 \r
403             // Watch for genesis block and best block\r
404             if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == hashGenesisBlock)\r
405                 pindexGenesisBlock = pindexNew;\r
406         }\r
407         else\r
408         {\r
409             break;\r
410         }\r
411     }\r
412     pcursor->close();\r
413 \r
414     if (!ReadHashBestChain(hashBestChain))\r
415     {\r
416         if (pindexGenesisBlock == NULL)\r
417             return true;\r
418         return error("CTxDB::LoadBlockIndex() : hashBestChain not found");\r
419     }\r
420 \r
421     if (!mapBlockIndex.count(hashBestChain))\r
422         return error("CTxDB::LoadBlockIndex() : blockindex for hashBestChain not found");\r
423     pindexBest = mapBlockIndex[hashBestChain];\r
424     nBestHeight = pindexBest->nHeight;\r
425     printf("LoadBlockIndex(): hashBestChain=%s  height=%d\n", hashBestChain.ToString().substr(0,16).c_str(), nBestHeight);\r
426 \r
427     return true;\r
428 }\r
429 \r
430 \r
431 \r
432 \r
433 \r
434 //\r
435 // CAddrDB\r
436 //\r
437 \r
438 bool CAddrDB::WriteAddress(const CAddress& addr)\r
439 {\r
440     return Write(make_pair(string("addr"), addr.GetKey()), addr);\r
441 }\r
442 \r
443 bool CAddrDB::LoadAddresses()\r
444 {\r
445     CRITICAL_BLOCK(cs_mapAddresses)\r
446     {\r
447         // Load user provided addresses\r
448         CAutoFile filein = fopen("addr.txt", "rt");\r
449         if (filein)\r
450         {\r
451             try\r
452             {\r
453                 char psz[1000];\r
454                 while (fgets(psz, sizeof(psz), filein))\r
455                 {\r
456                     CAddress addr(psz, NODE_NETWORK);\r
457                     if (addr.IsValid())\r
458                         AddAddress(*this, addr);\r
459                 }\r
460             }\r
461             catch (...) { }\r
462         }\r
463 \r
464         // Get cursor\r
465         Dbc* pcursor = GetCursor();\r
466         if (!pcursor)\r
467             return false;\r
468 \r
469         loop\r
470         {\r
471             // Read next record\r
472             CDataStream ssKey;\r
473             CDataStream ssValue;\r
474             int ret = ReadAtCursor(pcursor, ssKey, ssValue);\r
475             if (ret == DB_NOTFOUND)\r
476                 break;\r
477             else if (ret != 0)\r
478                 return false;\r
479 \r
480             // Unserialize\r
481             string strType;\r
482             ssKey >> strType;\r
483             if (strType == "addr")\r
484             {\r
485                 CAddress addr;\r
486                 ssValue >> addr;\r
487                 mapAddresses.insert(make_pair(addr.GetKey(), addr));\r
488             }\r
489         }\r
490         pcursor->close();\r
491 \r
492         printf("Loaded %d addresses\n", mapAddresses.size());\r
493 \r
494         // Fix for possible bug that manifests in mapAddresses.count in irc.cpp,\r
495         // just need to call count here and it doesn't happen there.  The bug was the\r
496         // pack pragma in irc.cpp and has been fixed, but I'm not in a hurry to delete this.\r
497         mapAddresses.count(vector<unsigned char>(18));\r
498     }\r
499 \r
500     return true;\r
501 }\r
502 \r
503 bool LoadAddresses()\r
504 {\r
505     return CAddrDB("cr+").LoadAddresses();\r
506 }\r
507 \r
508 \r
509 \r
510 \r
511 //\r
512 // CReviewDB\r
513 //\r
514 \r
515 bool CReviewDB::ReadReviews(uint256 hash, vector<CReview>& vReviews)\r
516 {\r
517     vReviews.size(); // msvc workaround, just need to do anything with vReviews\r
518     return Read(make_pair(string("reviews"), hash), vReviews);\r
519 }\r
520 \r
521 bool CReviewDB::WriteReviews(uint256 hash, const vector<CReview>& vReviews)\r
522 {\r
523     return Write(make_pair(string("reviews"), hash), vReviews);\r
524 }\r
525 \r
526 \r
527 \r
528 \r
529 \r
530 \r
531 \r
532 //\r
533 // CWalletDB\r
534 //\r
535 \r
536 bool CWalletDB::LoadWallet(vector<unsigned char>& vchDefaultKeyRet)\r
537 {\r
538     vchDefaultKeyRet.clear();\r
539 \r
540     // Modify defaults\r
541 #ifndef __WXMSW__\r
542     // Reports that tray icon can disappear on gnome, 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")\r
597             {\r
598                 vector<unsigned char> vchPubKey;\r
599                 ssKey >> vchPubKey;\r
600                 CPrivKey vchPrivKey;\r
601                 ssValue >> vchPrivKey;\r
602 \r
603                 mapKeys[vchPubKey] = vchPrivKey;\r
604                 mapPubKeys[Hash160(vchPubKey)] = vchPubKey;\r
605             }\r
606             else if (strType == "defaultkey")\r
607             {\r
608                 ssValue >> vchDefaultKeyRet;\r
609             }\r
610             else if (strType == "setting")\r
611             {\r
612                 string strKey;\r
613                 ssKey >> strKey;\r
614 \r
615                 // Menu state\r
616                 if (strKey == "fShowGenerated")     ssValue >> fShowGenerated;\r
617                 if (strKey == "fGenerateBitcoins")  ssValue >> fGenerateBitcoins;\r
618 \r
619                 // Options\r
620                 if (strKey == "nTransactionFee")    ssValue >> nTransactionFee;\r
621                 if (strKey == "addrIncoming")       ssValue >> addrIncoming;\r
622                 if (strKey == "fLimitProcessors")   ssValue >> fLimitProcessors;\r
623                 if (strKey == "nLimitProcessors")   ssValue >> nLimitProcessors;\r
624                 if (strKey == "fMinimizeToTray")    ssValue >> fMinimizeToTray;\r
625                 if (strKey == "fMinimizeOnClose")   ssValue >> fMinimizeOnClose;\r
626                 if (strKey == "fUseProxy")          ssValue >> fUseProxy;\r
627                 if (strKey == "addrProxy")          ssValue >> addrProxy;\r
628 \r
629             }\r
630         }\r
631         pcursor->close();\r
632     }\r
633 \r
634     printf("fShowGenerated = %d\n", fShowGenerated);\r
635     printf("fGenerateBitcoins = %d\n", fGenerateBitcoins);\r
636     printf("nTransactionFee = %"PRI64d"\n", nTransactionFee);\r
637     printf("addrIncoming = %s\n", addrIncoming.ToString().c_str());\r
638     printf("fMinimizeToTray = %d\n", fMinimizeToTray);\r
639     printf("fMinimizeOnClose = %d\n", fMinimizeOnClose);\r
640     printf("fUseProxy = %d\n", fUseProxy);\r
641     printf("addrProxy = %s\n", addrProxy.ToString().c_str());\r
642 \r
643 \r
644     // The transaction fee setting won't be needed for many years to come.\r
645     // Setting it to zero here in case they set it to something in an earlier version.\r
646     if (nTransactionFee != 0)\r
647     {\r
648         nTransactionFee = 0;\r
649         WriteSetting("nTransactionFee", nTransactionFee);\r
650     }\r
651 \r
652     return true;\r
653 }\r
654 \r
655 bool LoadWallet(bool& fFirstRunRet)\r
656 {\r
657     fFirstRunRet = false;\r
658     vector<unsigned char> vchDefaultKey;\r
659     if (!CWalletDB("cr").LoadWallet(vchDefaultKey))\r
660         return false;\r
661     fFirstRunRet = vchDefaultKey.empty();\r
662 \r
663     if (mapKeys.count(vchDefaultKey))\r
664     {\r
665         // Set keyUser\r
666         keyUser.SetPubKey(vchDefaultKey);\r
667         keyUser.SetPrivKey(mapKeys[vchDefaultKey]);\r
668     }\r
669     else\r
670     {\r
671         // Create new keyUser and set as default key\r
672         RandAddSeedPerfmon();\r
673         keyUser.MakeNewKey();\r
674         if (!AddKey(keyUser))\r
675             return false;\r
676         if (!SetAddressBookName(PubKeyToAddress(keyUser.GetPubKey()), "Your Address"))\r
677             return false;\r
678         CWalletDB().WriteDefaultKey(keyUser.GetPubKey());\r
679     }\r
680 \r
681     CreateThread(ThreadFlushWalletDB, NULL);\r
682     return true;\r
683 }\r
684 \r
685 void ThreadFlushWalletDB(void* parg)\r
686 {\r
687     static bool fOneThread;\r
688     if (fOneThread)\r
689         return;\r
690     fOneThread = true;\r
691     if (mapArgs.count("-noflushwallet"))\r
692         return;\r
693 \r
694     unsigned int nLastSeen = nWalletDBUpdated;\r
695     unsigned int nLastFlushed = nWalletDBUpdated;\r
696     int64 nLastWalletUpdate = GetTime();\r
697     while (!fShutdown)\r
698     {\r
699         Sleep(500);\r
700 \r
701         if (nLastSeen != nWalletDBUpdated)\r
702         {\r
703             nLastSeen = nWalletDBUpdated;\r
704             nLastWalletUpdate = GetTime();\r
705         }\r
706 \r
707         if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2)\r
708         {\r
709             TRY_CRITICAL_BLOCK(cs_db)\r
710             {\r
711                 // Don't do this if any databases are in use\r
712                 int nRefCount = 0;\r
713                 map<string, int>::iterator mi = mapFileUseCount.begin();\r
714                 while (mi != mapFileUseCount.end())\r
715                 {\r
716                     nRefCount += (*mi).second;\r
717                     mi++;\r
718                 }\r
719 \r
720                 if (nRefCount == 0 && !fShutdown)\r
721                 {\r
722                     string strFile = "wallet.dat";\r
723                     map<string, int>::iterator mi = mapFileUseCount.find(strFile);\r
724                     if (mi != mapFileUseCount.end())\r
725                     {\r
726                         printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str());\r
727                         printf("Flushing wallet.dat\n");\r
728                         nLastFlushed = nWalletDBUpdated;\r
729                         int64 nStart = GetTimeMillis();\r
730 \r
731                         // Flush wallet.dat so it's self contained\r
732                         CloseDb(strFile);\r
733                         dbenv.txn_checkpoint(0, 0, 0);\r
734                         dbenv.lsn_reset(strFile.c_str(), 0);\r
735 \r
736                         mapFileUseCount.erase(mi++);\r
737                         printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart);\r
738                     }\r
739                 }\r
740             }\r
741         }\r
742     }\r
743 }\r