do an extra CheckBlock in ConnectBlock
[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             filesystem::create_directory(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 \r
134     // Flush database activity from memory pool to disk log\r
135     unsigned int nMinutes = 0;\r
136     if (strFile == "addr.dat")\r
137         nMinutes = 2;\r
138     if (strFile == "blkindex.dat" && IsInitialBlockDownload() && nBestHeight % 500 != 0)\r
139         nMinutes = 1;\r
140     dbenv.txn_checkpoint(0, nMinutes, 0);\r
141 \r
142     CRITICAL_BLOCK(cs_db)\r
143         --mapFileUseCount[strFile];\r
144 }\r
145 \r
146 void CloseDb(const string& strFile)\r
147 {\r
148     CRITICAL_BLOCK(cs_db)\r
149     {\r
150         if (mapDb[strFile] != NULL)\r
151         {\r
152             // Close the database handle\r
153             Db* pdb = mapDb[strFile];\r
154             pdb->close(0);\r
155             delete pdb;\r
156             mapDb[strFile] = NULL;\r
157         }\r
158     }\r
159 }\r
160 \r
161 void DBFlush(bool fShutdown)\r
162 {\r
163     // Flush log data to the actual data file\r
164     //  on all files that are not in use\r
165     printf("DBFlush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started");\r
166     if (!fDbEnvInit)\r
167         return;\r
168     CRITICAL_BLOCK(cs_db)\r
169     {\r
170         map<string, int>::iterator mi = mapFileUseCount.begin();\r
171         while (mi != mapFileUseCount.end())\r
172         {\r
173             string strFile = (*mi).first;\r
174             int nRefCount = (*mi).second;\r
175             printf("%s refcount=%d\n", strFile.c_str(), nRefCount);\r
176             if (nRefCount == 0)\r
177             {\r
178                 // Move log data to the dat file\r
179                 CloseDb(strFile);\r
180                 dbenv.txn_checkpoint(0, 0, 0);\r
181                 printf("%s flush\n", strFile.c_str());\r
182                 dbenv.lsn_reset(strFile.c_str(), 0);\r
183                 mapFileUseCount.erase(mi++);\r
184             }\r
185             else\r
186                 mi++;\r
187         }\r
188         if (fShutdown)\r
189         {\r
190             char** listp;\r
191             if (mapFileUseCount.empty())\r
192                 dbenv.log_archive(&listp, DB_ARCH_REMOVE);\r
193             dbenv.close(0);\r
194             fDbEnvInit = false;\r
195         }\r
196     }\r
197 }\r
198 \r
199 \r
200 \r
201 \r
202 \r
203 \r
204 //\r
205 // CTxDB\r
206 //\r
207 \r
208 bool CTxDB::ReadTxIndex(uint256 hash, CTxIndex& txindex)\r
209 {\r
210     assert(!fClient);\r
211     txindex.SetNull();\r
212     return Read(make_pair(string("tx"), hash), txindex);\r
213 }\r
214 \r
215 bool CTxDB::UpdateTxIndex(uint256 hash, const CTxIndex& txindex)\r
216 {\r
217     assert(!fClient);\r
218     return Write(make_pair(string("tx"), hash), txindex);\r
219 }\r
220 \r
221 bool CTxDB::AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight)\r
222 {\r
223     assert(!fClient);\r
224 \r
225     // Add to tx index\r
226     uint256 hash = tx.GetHash();\r
227     CTxIndex txindex(pos, tx.vout.size());\r
228     return Write(make_pair(string("tx"), hash), txindex);\r
229 }\r
230 \r
231 bool CTxDB::EraseTxIndex(const CTransaction& tx)\r
232 {\r
233     assert(!fClient);\r
234     uint256 hash = tx.GetHash();\r
235 \r
236     return Erase(make_pair(string("tx"), hash));\r
237 }\r
238 \r
239 bool CTxDB::ContainsTx(uint256 hash)\r
240 {\r
241     assert(!fClient);\r
242     return Exists(make_pair(string("tx"), hash));\r
243 }\r
244 \r
245 bool CTxDB::ReadOwnerTxes(uint160 hash160, int nMinHeight, vector<CTransaction>& vtx)\r
246 {\r
247     assert(!fClient);\r
248     vtx.clear();\r
249 \r
250     // Get cursor\r
251     Dbc* pcursor = GetCursor();\r
252     if (!pcursor)\r
253         return false;\r
254 \r
255     unsigned int fFlags = DB_SET_RANGE;\r
256     loop\r
257     {\r
258         // Read next record\r
259         CDataStream ssKey;\r
260         if (fFlags == DB_SET_RANGE)\r
261             ssKey << string("owner") << hash160 << CDiskTxPos(0, 0, 0);\r
262         CDataStream ssValue;\r
263         int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);\r
264         fFlags = DB_NEXT;\r
265         if (ret == DB_NOTFOUND)\r
266             break;\r
267         else if (ret != 0)\r
268         {\r
269             pcursor->close();\r
270             return false;\r
271         }\r
272 \r
273         // Unserialize\r
274         string strType;\r
275         uint160 hashItem;\r
276         CDiskTxPos pos;\r
277         ssKey >> strType >> hashItem >> pos;\r
278         int nItemHeight;\r
279         ssValue >> nItemHeight;\r
280 \r
281         // Read transaction\r
282         if (strType != "owner" || hashItem != hash160)\r
283             break;\r
284         if (nItemHeight >= nMinHeight)\r
285         {\r
286             vtx.resize(vtx.size()+1);\r
287             if (!vtx.back().ReadFromDisk(pos))\r
288             {\r
289                 pcursor->close();\r
290                 return false;\r
291             }\r
292         }\r
293     }\r
294 \r
295     pcursor->close();\r
296     return true;\r
297 }\r
298 \r
299 bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex)\r
300 {\r
301     assert(!fClient);\r
302     tx.SetNull();\r
303     if (!ReadTxIndex(hash, txindex))\r
304         return false;\r
305     return (tx.ReadFromDisk(txindex.pos));\r
306 }\r
307 \r
308 bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx)\r
309 {\r
310     CTxIndex txindex;\r
311     return ReadDiskTx(hash, tx, txindex);\r
312 }\r
313 \r
314 bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex)\r
315 {\r
316     return ReadDiskTx(outpoint.hash, tx, txindex);\r
317 }\r
318 \r
319 bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx)\r
320 {\r
321     CTxIndex txindex;\r
322     return ReadDiskTx(outpoint.hash, tx, txindex);\r
323 }\r
324 \r
325 bool CTxDB::WriteBlockIndex(const CDiskBlockIndex& blockindex)\r
326 {\r
327     return Write(make_pair(string("blockindex"), blockindex.GetBlockHash()), blockindex);\r
328 }\r
329 \r
330 bool CTxDB::EraseBlockIndex(uint256 hash)\r
331 {\r
332     return Erase(make_pair(string("blockindex"), hash));\r
333 }\r
334 \r
335 bool CTxDB::ReadHashBestChain(uint256& hashBestChain)\r
336 {\r
337     return Read(string("hashBestChain"), hashBestChain);\r
338 }\r
339 \r
340 bool CTxDB::WriteHashBestChain(uint256 hashBestChain)\r
341 {\r
342     return Write(string("hashBestChain"), hashBestChain);\r
343 }\r
344 \r
345 bool CTxDB::ReadBestInvalidWork(CBigNum& bnBestInvalidWork)\r
346 {\r
347     return Read(string("bnBestInvalidWork"), bnBestInvalidWork);\r
348 }\r
349 \r
350 bool CTxDB::WriteBestInvalidWork(CBigNum bnBestInvalidWork)\r
351 {\r
352     return Write(string("bnBestInvalidWork"), bnBestInvalidWork);\r
353 }\r
354 \r
355 CBlockIndex* InsertBlockIndex(uint256 hash)\r
356 {\r
357     if (hash == 0)\r
358         return NULL;\r
359 \r
360     // Return existing\r
361     map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash);\r
362     if (mi != mapBlockIndex.end())\r
363         return (*mi).second;\r
364 \r
365     // Create new\r
366     CBlockIndex* pindexNew = new CBlockIndex();\r
367     if (!pindexNew)\r
368         throw runtime_error("LoadBlockIndex() : new CBlockIndex failed");\r
369     mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;\r
370     pindexNew->phashBlock = &((*mi).first);\r
371 \r
372     return pindexNew;\r
373 }\r
374 \r
375 bool CTxDB::LoadBlockIndex()\r
376 {\r
377     // Get database cursor\r
378     Dbc* pcursor = GetCursor();\r
379     if (!pcursor)\r
380         return false;\r
381 \r
382     // Load mapBlockIndex\r
383     unsigned int fFlags = DB_SET_RANGE;\r
384     loop\r
385     {\r
386         // Read next record\r
387         CDataStream ssKey;\r
388         if (fFlags == DB_SET_RANGE)\r
389             ssKey << make_pair(string("blockindex"), uint256(0));\r
390         CDataStream ssValue;\r
391         int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);\r
392         fFlags = DB_NEXT;\r
393         if (ret == DB_NOTFOUND)\r
394             break;\r
395         else if (ret != 0)\r
396             return false;\r
397 \r
398         // Unserialize\r
399         string strType;\r
400         ssKey >> strType;\r
401         if (strType == "blockindex")\r
402         {\r
403             CDiskBlockIndex diskindex;\r
404             ssValue >> diskindex;\r
405 \r
406             // Construct block index object\r
407             CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash());\r
408             pindexNew->pprev          = InsertBlockIndex(diskindex.hashPrev);\r
409             pindexNew->pnext          = InsertBlockIndex(diskindex.hashNext);\r
410             pindexNew->nFile          = diskindex.nFile;\r
411             pindexNew->nBlockPos      = diskindex.nBlockPos;\r
412             pindexNew->nHeight        = diskindex.nHeight;\r
413             pindexNew->nVersion       = diskindex.nVersion;\r
414             pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;\r
415             pindexNew->nTime          = diskindex.nTime;\r
416             pindexNew->nBits          = diskindex.nBits;\r
417             pindexNew->nNonce         = diskindex.nNonce;\r
418 \r
419             // Watch for genesis block\r
420             if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == hashGenesisBlock)\r
421                 pindexGenesisBlock = pindexNew;\r
422         }\r
423         else\r
424         {\r
425             break;\r
426         }\r
427     }\r
428     pcursor->close();\r
429 \r
430     // Calculate bnChainWork\r
431     vector<pair<int, CBlockIndex*> > vSortedByHeight;\r
432     vSortedByHeight.reserve(mapBlockIndex.size());\r
433     foreach(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex)\r
434     {\r
435         CBlockIndex* pindex = item.second;\r
436         vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex));\r
437     }\r
438     sort(vSortedByHeight.begin(), vSortedByHeight.end());\r
439     foreach(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight)\r
440     {\r
441         CBlockIndex* pindex = item.second;\r
442         pindex->bnChainWork = (pindex->pprev ? pindex->pprev->bnChainWork : 0) + pindex->GetBlockWork();\r
443     }\r
444 \r
445     // Load hashBestChain pointer to end of best chain\r
446     if (!ReadHashBestChain(hashBestChain))\r
447     {\r
448         if (pindexGenesisBlock == NULL)\r
449             return true;\r
450         return error("CTxDB::LoadBlockIndex() : hashBestChain not loaded");\r
451     }\r
452     if (!mapBlockIndex.count(hashBestChain))\r
453         return error("CTxDB::LoadBlockIndex() : hashBestChain not found in the block index");\r
454     pindexBest = mapBlockIndex[hashBestChain];\r
455     nBestHeight = pindexBest->nHeight;\r
456     bnBestChainWork = pindexBest->bnChainWork;\r
457     printf("LoadBlockIndex(): hashBestChain=%s  height=%d\n", hashBestChain.ToString().substr(0,16).c_str(), nBestHeight);\r
458 \r
459     // Load bnBestInvalidWork, OK if it doesn't exist\r
460     ReadBestInvalidWork(bnBestInvalidWork);\r
461 \r
462     // Verify blocks in the best chain\r
463     CBlockIndex* pindexFork = NULL;\r
464     for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev)\r
465     {\r
466         CBlock block;\r
467         if (!block.ReadFromDisk(pindex))\r
468             return error("LoadBlockIndex() : block.ReadFromDisk failed");\r
469         if (!block.CheckBlock())\r
470         {\r
471             printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());\r
472             pindexFork = pindex->pprev;\r
473         }\r
474     }\r
475     if (pindexFork)\r
476     {\r
477         // Reorg back to the fork\r
478         printf("LoadBlockIndex() : *** moving best chain pointer back to block %d\n", pindexFork->nHeight);\r
479         CBlock block;\r
480         if (!block.ReadFromDisk(pindexFork))\r
481             return error("LoadBlockIndex() : block.ReadFromDisk failed");\r
482         CTxDB txdb;\r
483         block.SetBestChain(txdb, pindexFork);\r
484     }\r
485 \r
486     return true;\r
487 }\r
488 \r
489 \r
490 \r
491 \r
492 \r
493 //\r
494 // CAddrDB\r
495 //\r
496 \r
497 bool CAddrDB::WriteAddress(const CAddress& addr)\r
498 {\r
499     return Write(make_pair(string("addr"), addr.GetKey()), addr);\r
500 }\r
501 \r
502 bool CAddrDB::LoadAddresses()\r
503 {\r
504     CRITICAL_BLOCK(cs_mapAddresses)\r
505     {\r
506         // Load user provided addresses\r
507         CAutoFile filein = fopen((GetDataDir() + "/addr.txt").c_str(), "rt");\r
508         if (filein)\r
509         {\r
510             try\r
511             {\r
512                 char psz[1000];\r
513                 while (fgets(psz, sizeof(psz), filein))\r
514                 {\r
515                     CAddress addr(psz, NODE_NETWORK);\r
516                     addr.nTime = 0; // so it won't relay unless successfully connected\r
517                     if (addr.IsValid())\r
518                         AddAddress(addr);\r
519                 }\r
520             }\r
521             catch (...) { }\r
522         }\r
523 \r
524         // Get cursor\r
525         Dbc* pcursor = GetCursor();\r
526         if (!pcursor)\r
527             return false;\r
528 \r
529         loop\r
530         {\r
531             // Read next record\r
532             CDataStream ssKey;\r
533             CDataStream ssValue;\r
534             int ret = ReadAtCursor(pcursor, ssKey, ssValue);\r
535             if (ret == DB_NOTFOUND)\r
536                 break;\r
537             else if (ret != 0)\r
538                 return false;\r
539 \r
540             // Unserialize\r
541             string strType;\r
542             ssKey >> strType;\r
543             if (strType == "addr")\r
544             {\r
545                 CAddress addr;\r
546                 ssValue >> addr;\r
547                 mapAddresses.insert(make_pair(addr.GetKey(), addr));\r
548             }\r
549         }\r
550         pcursor->close();\r
551 \r
552         printf("Loaded %d addresses\n", mapAddresses.size());\r
553 \r
554         // Fix for possible bug that manifests in mapAddresses.count in irc.cpp,\r
555         // just need to call count here and it doesn't happen there.  The bug was the\r
556         // pack pragma in irc.cpp and has been fixed, but I'm not in a hurry to delete this.\r
557         mapAddresses.count(vector<unsigned char>(18));\r
558     }\r
559 \r
560     return true;\r
561 }\r
562 \r
563 bool LoadAddresses()\r
564 {\r
565     return CAddrDB("cr+").LoadAddresses();\r
566 }\r
567 \r
568 \r
569 \r
570 \r
571 //\r
572 // CWalletDB\r
573 //\r
574 \r
575 bool CWalletDB::LoadWallet()\r
576 {\r
577     vchDefaultKey.clear();\r
578     int nFileVersion = 0;\r
579 \r
580     // Modify defaults\r
581 #ifndef __WXMSW__\r
582     // Tray icon sometimes disappears on 9.10 karmic koala 64-bit, leaving no way to access the program\r
583     fMinimizeToTray = false;\r
584     fMinimizeOnClose = false;\r
585 #endif\r
586 \r
587     //// todo: shouldn't we catch exceptions and try to recover and continue?\r
588     CRITICAL_BLOCK(cs_mapKeys)\r
589     CRITICAL_BLOCK(cs_mapWallet)\r
590     {\r
591         // Get cursor\r
592         Dbc* pcursor = GetCursor();\r
593         if (!pcursor)\r
594             return false;\r
595 \r
596         loop\r
597         {\r
598             // Read next record\r
599             CDataStream ssKey;\r
600             CDataStream ssValue;\r
601             int ret = ReadAtCursor(pcursor, ssKey, ssValue);\r
602             if (ret == DB_NOTFOUND)\r
603                 break;\r
604             else if (ret != 0)\r
605                 return false;\r
606 \r
607             // Unserialize\r
608             // Taking advantage of the fact that pair serialization\r
609             // is just the two items serialized one after the other\r
610             string strType;\r
611             ssKey >> strType;\r
612             if (strType == "name")\r
613             {\r
614                 string strAddress;\r
615                 ssKey >> strAddress;\r
616                 ssValue >> mapAddressBook[strAddress];\r
617             }\r
618             else if (strType == "tx")\r
619             {\r
620                 uint256 hash;\r
621                 ssKey >> hash;\r
622                 CWalletTx& wtx = mapWallet[hash];\r
623                 ssValue >> wtx;\r
624 \r
625                 if (wtx.GetHash() != hash)\r
626                     printf("Error in wallet.dat, hash mismatch\n");\r
627 \r
628                 //// debug print\r
629                 //printf("LoadWallet  %s\n", wtx.GetHash().ToString().c_str());\r
630                 //printf(" %12I64d  %s  %s  %s\n",\r
631                 //    wtx.vout[0].nValue,\r
632                 //    DateTimeStrFormat("%x %H:%M:%S", wtx.nTime).c_str(),\r
633                 //    wtx.hashBlock.ToString().substr(0,16).c_str(),\r
634                 //    wtx.mapValue["message"].c_str());\r
635             }\r
636             else if (strType == "key" || strType == "wkey")\r
637             {\r
638                 vector<unsigned char> vchPubKey;\r
639                 ssKey >> vchPubKey;\r
640                 CWalletKey wkey;\r
641                 if (strType == "key")\r
642                     ssValue >> wkey.vchPrivKey;\r
643                 else\r
644                     ssValue >> wkey;\r
645 \r
646                 mapKeys[vchPubKey] = wkey.vchPrivKey;\r
647                 mapPubKeys[Hash160(vchPubKey)] = vchPubKey;\r
648             }\r
649             else if (strType == "defaultkey")\r
650             {\r
651                 ssValue >> vchDefaultKey;\r
652             }\r
653             else if (strType == "version")\r
654             {\r
655                 ssValue >> nFileVersion;\r
656                 if (nFileVersion == 10300)\r
657                     nFileVersion = 300;\r
658             }\r
659             else if (strType == "setting")\r
660             {\r
661                 string strKey;\r
662                 ssKey >> strKey;\r
663 \r
664                 // Menu state\r
665                 if (strKey == "fGenerateBitcoins")  ssValue >> fGenerateBitcoins;\r
666 \r
667                 // Options\r
668                 if (strKey == "nTransactionFee")    ssValue >> nTransactionFee;\r
669                 if (strKey == "addrIncoming")       ssValue >> addrIncoming;\r
670                 if (strKey == "fLimitProcessors")   ssValue >> fLimitProcessors;\r
671                 if (strKey == "nLimitProcessors")   ssValue >> nLimitProcessors;\r
672                 if (strKey == "fMinimizeToTray")    ssValue >> fMinimizeToTray;\r
673                 if (strKey == "fMinimizeOnClose")   ssValue >> fMinimizeOnClose;\r
674                 if (strKey == "fUseProxy")          ssValue >> fUseProxy;\r
675                 if (strKey == "addrProxy")          ssValue >> addrProxy;\r
676 \r
677             }\r
678         }\r
679         pcursor->close();\r
680     }\r
681 \r
682     printf("nFileVersion = %d\n", nFileVersion);\r
683     printf("fGenerateBitcoins = %d\n", fGenerateBitcoins);\r
684     printf("nTransactionFee = %"PRI64d"\n", nTransactionFee);\r
685     printf("addrIncoming = %s\n", addrIncoming.ToString().c_str());\r
686     printf("fMinimizeToTray = %d\n", fMinimizeToTray);\r
687     printf("fMinimizeOnClose = %d\n", fMinimizeOnClose);\r
688     printf("fUseProxy = %d\n", fUseProxy);\r
689     printf("addrProxy = %s\n", addrProxy.ToString().c_str());\r
690 \r
691 \r
692     // The transaction fee setting won't be needed for many years to come.\r
693     // Setting it to zero here in case they set it to something in an earlier version.\r
694     if (nTransactionFee != 0)\r
695     {\r
696         nTransactionFee = 0;\r
697         WriteSetting("nTransactionFee", nTransactionFee);\r
698     }\r
699 \r
700     // Upgrade\r
701     if (nFileVersion < VERSION)\r
702     {\r
703         // Get rid of old debug.log file in current directory\r
704         if (nFileVersion <= 105 && !pszSetDataDir[0])\r
705             unlink("debug.log");\r
706 \r
707         WriteVersion(VERSION);\r
708     }\r
709 \r
710     return true;\r
711 }\r
712 \r
713 bool LoadWallet(bool& fFirstRunRet)\r
714 {\r
715     fFirstRunRet = false;\r
716     if (!CWalletDB("cr+").LoadWallet())\r
717         return false;\r
718     fFirstRunRet = vchDefaultKey.empty();\r
719 \r
720     if (mapKeys.count(vchDefaultKey))\r
721     {\r
722         // Set keyUser\r
723         keyUser.SetPubKey(vchDefaultKey);\r
724         keyUser.SetPrivKey(mapKeys[vchDefaultKey]);\r
725     }\r
726     else\r
727     {\r
728         // Create new keyUser and set as default key\r
729         RandAddSeedPerfmon();\r
730         keyUser.MakeNewKey();\r
731         if (!AddKey(keyUser))\r
732             return false;\r
733         if (!SetAddressBookName(PubKeyToAddress(keyUser.GetPubKey()), "Your Address"))\r
734             return false;\r
735         CWalletDB().WriteDefaultKey(keyUser.GetPubKey());\r
736     }\r
737 \r
738     CreateThread(ThreadFlushWalletDB, NULL);\r
739     return true;\r
740 }\r
741 \r
742 void ThreadFlushWalletDB(void* parg)\r
743 {\r
744     static bool fOneThread;\r
745     if (fOneThread)\r
746         return;\r
747     fOneThread = true;\r
748     if (mapArgs.count("-noflushwallet"))\r
749         return;\r
750 \r
751     unsigned int nLastSeen = nWalletDBUpdated;\r
752     unsigned int nLastFlushed = nWalletDBUpdated;\r
753     int64 nLastWalletUpdate = GetTime();\r
754     while (!fShutdown)\r
755     {\r
756         Sleep(500);\r
757 \r
758         if (nLastSeen != nWalletDBUpdated)\r
759         {\r
760             nLastSeen = nWalletDBUpdated;\r
761             nLastWalletUpdate = GetTime();\r
762         }\r
763 \r
764         if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2)\r
765         {\r
766             TRY_CRITICAL_BLOCK(cs_db)\r
767             {\r
768                 // Don't do this if any databases are in use\r
769                 int nRefCount = 0;\r
770                 map<string, int>::iterator mi = mapFileUseCount.begin();\r
771                 while (mi != mapFileUseCount.end())\r
772                 {\r
773                     nRefCount += (*mi).second;\r
774                     mi++;\r
775                 }\r
776 \r
777                 if (nRefCount == 0 && !fShutdown)\r
778                 {\r
779                     string strFile = "wallet.dat";\r
780                     map<string, int>::iterator mi = mapFileUseCount.find(strFile);\r
781                     if (mi != mapFileUseCount.end())\r
782                     {\r
783                         printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str());\r
784                         printf("Flushing wallet.dat\n");\r
785                         nLastFlushed = nWalletDBUpdated;\r
786                         int64 nStart = GetTimeMillis();\r
787 \r
788                         // Flush wallet.dat so it's self contained\r
789                         CloseDb(strFile);\r
790                         dbenv.txn_checkpoint(0, 0, 0);\r
791                         dbenv.lsn_reset(strFile.c_str(), 0);\r
792 \r
793                         mapFileUseCount.erase(mi++);\r
794                         printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart);\r
795                     }\r
796                 }\r
797             }\r
798         }\r
799     }\r
800 }\r