Projects : bitcoin : bitcoin_dumpblock_no_losers

bitcoin/src/db.cpp

Dir - Raw

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