Projects : bitcoin : bitcoin_getblockindex_etc

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