Back to home page

Bitcoin sources

 
 

    


0001 // Copyright (c) 2009-2010 Satoshi Nakamoto
0002 // Copyright (c) 2009-2012 The Bitcoin developers
0003 // Distributed under the MIT/X11 software license, see the accompanying
0004 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
0005 
0006 #include "headers.h"
0007 #include "db.h"
0008 #include "net.h"
0009 #include <boost/filesystem.hpp>
0010 #include <boost/filesystem/fstream.hpp>
0011 
0012 using namespace std;
0013 using namespace boost;
0014 
0015 
0016 unsigned int nWalletDBUpdated;
0017 uint64 nAccountingEntryNumber = 0;
0018 
0019 
0020 
0021 //
0022 // CDB
0023 //
0024 
0025 static CCriticalSection cs_db;
0026 static bool fDbEnvInit = false;
0027 DbEnv dbenv(0);
0028 static map<string, int> mapFileUseCount;
0029 static map<string, Db*> mapDb;
0030 
0031 static void EnvShutdown()
0032 {
0033     if (!fDbEnvInit)
0034         return;
0035 
0036     fDbEnvInit = false;
0037     try
0038     {
0039         dbenv.close(0);
0040     }
0041     catch (const DbException& e)
0042     {
0043         printf("EnvShutdown exception: %s (%d)\n", e.what(), e.get_errno());
0044     }
0045     DbEnv(0).remove(GetDataDir().c_str(), 0);
0046 }
0047 
0048 class CDBInit
0049 {
0050 public:
0051     CDBInit()
0052     {
0053     }
0054     ~CDBInit()
0055     {
0056         EnvShutdown();
0057     }
0058 }
0059 instance_of_cdbinit;
0060 
0061 
0062 CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL)
0063 {
0064     int ret;
0065     if (pszFile == NULL)
0066         return;
0067 
0068     fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
0069     bool fCreate = strchr(pszMode, 'c');
0070     unsigned int nFlags = DB_THREAD;
0071     if (fCreate)
0072         nFlags |= DB_CREATE;
0073 
0074     CRITICAL_BLOCK(cs_db)
0075     {
0076         if (!fDbEnvInit)
0077         {
0078             if (fShutdown)
0079                 return;
0080             string strDataDir = GetDataDir();
0081             string strLogDir = strDataDir + "/database";
0082             filesystem::create_directory(strLogDir.c_str());
0083             string strErrorFile = strDataDir + "/db.log";
0084             printf("dbenv.open strLogDir=%s strErrorFile=%s\n", strLogDir.c_str(), strErrorFile.c_str());
0085 
0086             dbenv.set_lg_dir(strLogDir.c_str());
0087             dbenv.set_lg_max(1000000);
0088             dbenv.set_lk_max_locks(40000);
0089             dbenv.set_lk_max_objects(40000);
0090             dbenv.set_errfile(fopen(strErrorFile.c_str(), "a")); /// debug
0091             dbenv.set_flags(DB_AUTO_COMMIT, 1);
0092             dbenv.set_flags(DB_TXN_WRITE_NOSYNC, 1);
0093             dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1);
0094             ret = dbenv.open(strDataDir.c_str(),
0095                              DB_CREATE     |
0096                              DB_INIT_LOCK  |
0097                              DB_INIT_LOG   |
0098                              DB_INIT_MPOOL |
0099                              DB_INIT_TXN   |
0100                              DB_THREAD     |
0101                              DB_RECOVER,
0102                              S_IRUSR | S_IWUSR);
0103             if (ret > 0)
0104                 throw runtime_error(strprintf("CDB() : error %d opening database environment", ret));
0105             fDbEnvInit = true;
0106         }
0107 
0108         strFile = pszFile;
0109         ++mapFileUseCount[strFile];
0110         pdb = mapDb[strFile];
0111         if (pdb == NULL)
0112         {
0113             pdb = new Db(&dbenv, 0);
0114 
0115             ret = pdb->open(NULL,      // Txn pointer
0116                             pszFile,   // Filename
0117                             "main",    // Logical db name
0118                             DB_BTREE,  // Database type
0119                             nFlags,    // Flags
0120                             0);
0121 
0122             if (ret > 0)
0123             {
0124                 delete pdb;
0125                 pdb = NULL;
0126                 CRITICAL_BLOCK(cs_db)
0127                     --mapFileUseCount[strFile];
0128                 strFile = "";
0129                 throw runtime_error(strprintf("CDB() : can't open database file %s, error %d", pszFile, ret));
0130             }
0131 
0132             if (fCreate && !Exists(string("version")))
0133             {
0134                 bool fTmp = fReadOnly;
0135                 fReadOnly = false;
0136                 WriteVersion(VERSION);
0137                 fReadOnly = fTmp;
0138             }
0139 
0140             mapDb[strFile] = pdb;
0141         }
0142     }
0143 }
0144 
0145 void CDB::Close()
0146 {
0147     if (!pdb)
0148         return;
0149     if (!vTxn.empty())
0150         vTxn.front()->abort();
0151     vTxn.clear();
0152     pdb = NULL;
0153 
0154     // Flush database activity from memory pool to disk log
0155     unsigned int nMinutes = 0;
0156     if (fReadOnly)
0157         nMinutes = 1;
0158     if (strFile == "addr.dat")
0159         nMinutes = 2;
0160     if (strFile == "blkindex.dat" && IsInitialBlockDownload() && nBestHeight % 500 != 0)
0161         nMinutes = 1;
0162     dbenv.txn_checkpoint(0, nMinutes, 0);
0163 
0164     CRITICAL_BLOCK(cs_db)
0165         --mapFileUseCount[strFile];
0166 }
0167 
0168 void static CloseDb(const string& strFile)
0169 {
0170     CRITICAL_BLOCK(cs_db)
0171     {
0172         if (mapDb[strFile] != NULL)
0173         {
0174             // Close the database handle
0175             Db* pdb = mapDb[strFile];
0176             pdb->close(0);
0177             delete pdb;
0178             mapDb[strFile] = NULL;
0179         }
0180     }
0181 }
0182 
0183 bool CDB::Rewrite(const string& strFile, const char* pszSkip)
0184 {
0185     while (!fShutdown)
0186     {
0187         CRITICAL_BLOCK(cs_db)
0188         {
0189             if (!mapFileUseCount.count(strFile) || mapFileUseCount[strFile] == 0)
0190             {
0191                 // Flush log data to the dat file
0192                 CloseDb(strFile);
0193                 dbenv.txn_checkpoint(0, 0, 0);
0194                 dbenv.lsn_reset(strFile.c_str(), 0);
0195                 mapFileUseCount.erase(strFile);
0196 
0197                 bool fSuccess = true;
0198                 printf("Rewriting %s...\n", strFile.c_str());
0199                 string strFileRes = strFile + ".rewrite";
0200                 { // surround usage of db with extra {}
0201                     CDB db(strFile.c_str(), "r");
0202                     Db* pdbCopy = new Db(&dbenv, 0);
0203     
0204                     int ret = pdbCopy->open(NULL,                 // Txn pointer
0205                                             strFileRes.c_str(),   // Filename
0206                                             "main",    // Logical db name
0207                                             DB_BTREE,  // Database type
0208                                             DB_CREATE,    // Flags
0209                                             0);
0210                     if (ret > 0)
0211                     {
0212                         printf("Cannot create database file %s\n", strFileRes.c_str());
0213                         fSuccess = false;
0214                     }
0215     
0216                     Dbc* pcursor = db.GetCursor();
0217                     if (pcursor)
0218                         while (fSuccess)
0219                         {
0220                             CDataStream ssKey;
0221                             CDataStream ssValue;
0222                             int ret = db.ReadAtCursor(pcursor, ssKey, ssValue, DB_NEXT);
0223                             if (ret == DB_NOTFOUND)
0224                             {
0225                                 pcursor->close();
0226                                 break;
0227                             }
0228                             else if (ret != 0)
0229                             {
0230                                 pcursor->close();
0231                                 fSuccess = false;
0232                                 break;
0233                             }
0234                             if (pszSkip &&
0235                                 strncmp(&ssKey[0], pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0)
0236                                 continue;
0237                             if (strncmp(&ssKey[0], "\x07version", 8) == 0)
0238                             {
0239                                 // Update version:
0240                                 ssValue.clear();
0241                                 ssValue << VERSION;
0242                             }
0243                             Dbt datKey(&ssKey[0], ssKey.size());
0244                             Dbt datValue(&ssValue[0], ssValue.size());
0245                             int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE);
0246                             if (ret2 > 0)
0247                                 fSuccess = false;
0248                         }
0249                     if (fSuccess)
0250                     {
0251                         db.Close();
0252                         CloseDb(strFile);
0253                         if (pdbCopy->close(0))
0254                             fSuccess = false;
0255                         delete pdbCopy;
0256                     }
0257                 }
0258                 if (fSuccess)
0259                 {
0260                     Db dbA(&dbenv, 0);
0261                     if (dbA.remove(strFile.c_str(), NULL, 0))
0262                         fSuccess = false;
0263                     Db dbB(&dbenv, 0);
0264                     if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0))
0265                         fSuccess = false;
0266                 }
0267                 if (!fSuccess)
0268                     printf("Rewriting of %s FAILED!\n", strFileRes.c_str());
0269                 return fSuccess;
0270             }
0271         }
0272         Sleep(100);
0273     }
0274     return false;
0275 }
0276 
0277 
0278 void DBFlush(bool fShutdown)
0279 {
0280     // Flush log data to the actual data file
0281     //  on all files that are not in use
0282     printf("DBFlush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started");
0283     if (!fDbEnvInit)
0284         return;
0285     CRITICAL_BLOCK(cs_db)
0286     {
0287         map<string, int>::iterator mi = mapFileUseCount.begin();
0288         while (mi != mapFileUseCount.end())
0289         {
0290             string strFile = (*mi).first;
0291             int nRefCount = (*mi).second;
0292             printf("%s refcount=%d\n", strFile.c_str(), nRefCount);
0293             if (nRefCount == 0)
0294             {
0295                 // Move log data to the dat file
0296                 CloseDb(strFile);
0297                 dbenv.txn_checkpoint(0, 0, 0);
0298                 printf("%s flush\n", strFile.c_str());
0299                 dbenv.lsn_reset(strFile.c_str(), 0);
0300                 mapFileUseCount.erase(mi++);
0301             }
0302             else
0303                 mi++;
0304         }
0305         if (fShutdown)
0306         {
0307             char** listp;
0308             if (mapFileUseCount.empty())
0309             {
0310                 dbenv.log_archive(&listp, DB_ARCH_REMOVE);
0311                 EnvShutdown();
0312             }
0313         }
0314     }
0315 }
0316 
0317 
0318 
0319 
0320 
0321 
0322 //
0323 // CTxDB
0324 //
0325 
0326 bool CTxDB::ReadTxIndex(uint256 hash, CTxIndex& txindex)
0327 {
0328     assert(!fClient);
0329     txindex.SetNull();
0330     return Read(make_pair(string("tx"), hash), txindex);
0331 }
0332 
0333 bool CTxDB::UpdateTxIndex(uint256 hash, const CTxIndex& txindex)
0334 {
0335     assert(!fClient);
0336     return Write(make_pair(string("tx"), hash), txindex);
0337 }
0338 
0339 bool CTxDB::AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight)
0340 {
0341     assert(!fClient);
0342 
0343     // Add to tx index
0344     uint256 hash = tx.GetHash();
0345     CTxIndex txindex(pos, tx.vout.size());
0346     return Write(make_pair(string("tx"), hash), txindex);
0347 }
0348 
0349 bool CTxDB::EraseTxIndex(const CTransaction& tx)
0350 {
0351     assert(!fClient);
0352     uint256 hash = tx.GetHash();
0353 
0354     return Erase(make_pair(string("tx"), hash));
0355 }
0356 
0357 bool CTxDB::ContainsTx(uint256 hash)
0358 {
0359     assert(!fClient);
0360     return Exists(make_pair(string("tx"), hash));
0361 }
0362 
0363 bool CTxDB::ReadOwnerTxes(uint160 hash160, int nMinHeight, vector<CTransaction>& vtx)
0364 {
0365     assert(!fClient);
0366     vtx.clear();
0367 
0368     // Get cursor
0369     Dbc* pcursor = GetCursor();
0370     if (!pcursor)
0371         return false;
0372 
0373     unsigned int fFlags = DB_SET_RANGE;
0374     loop
0375     {
0376         // Read next record
0377         CDataStream ssKey;
0378         if (fFlags == DB_SET_RANGE)
0379             ssKey << string("owner") << hash160 << CDiskTxPos(0, 0, 0);
0380         CDataStream ssValue;
0381         int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
0382         fFlags = DB_NEXT;
0383         if (ret == DB_NOTFOUND)
0384             break;
0385         else if (ret != 0)
0386         {
0387             pcursor->close();
0388             return false;
0389         }
0390 
0391         // Unserialize
0392         string strType;
0393         uint160 hashItem;
0394         CDiskTxPos pos;
0395         ssKey >> strType >> hashItem >> pos;
0396         int nItemHeight;
0397         ssValue >> nItemHeight;
0398 
0399         // Read transaction
0400         if (strType != "owner" || hashItem != hash160)
0401             break;
0402         if (nItemHeight >= nMinHeight)
0403         {
0404             vtx.resize(vtx.size()+1);
0405             if (!vtx.back().ReadFromDisk(pos))
0406             {
0407                 pcursor->close();
0408                 return false;
0409             }
0410         }
0411     }
0412 
0413     pcursor->close();
0414     return true;
0415 }
0416 
0417 bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex)
0418 {
0419     assert(!fClient);
0420     tx.SetNull();
0421     if (!ReadTxIndex(hash, txindex))
0422         return false;
0423     return (tx.ReadFromDisk(txindex.pos));
0424 }
0425 
0426 bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx)
0427 {
0428     CTxIndex txindex;
0429     return ReadDiskTx(hash, tx, txindex);
0430 }
0431 
0432 bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex)
0433 {
0434     return ReadDiskTx(outpoint.hash, tx, txindex);
0435 }
0436 
0437 bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx)
0438 {
0439     CTxIndex txindex;
0440     return ReadDiskTx(outpoint.hash, tx, txindex);
0441 }
0442 
0443 bool CTxDB::WriteBlockIndex(const CDiskBlockIndex& blockindex)
0444 {
0445     return Write(make_pair(string("blockindex"), blockindex.GetBlockHash()), blockindex);
0446 }
0447 
0448 bool CTxDB::EraseBlockIndex(uint256 hash)
0449 {
0450     return Erase(make_pair(string("blockindex"), hash));
0451 }
0452 
0453 bool CTxDB::ReadHashBestChain(uint256& hashBestChain)
0454 {
0455     return Read(string("hashBestChain"), hashBestChain);
0456 }
0457 
0458 bool CTxDB::WriteHashBestChain(uint256 hashBestChain)
0459 {
0460     return Write(string("hashBestChain"), hashBestChain);
0461 }
0462 
0463 bool CTxDB::ReadBestInvalidWork(CBigNum& bnBestInvalidWork)
0464 {
0465     return Read(string("bnBestInvalidWork"), bnBestInvalidWork);
0466 }
0467 
0468 bool CTxDB::WriteBestInvalidWork(CBigNum bnBestInvalidWork)
0469 {
0470     return Write(string("bnBestInvalidWork"), bnBestInvalidWork);
0471 }
0472 
0473 CBlockIndex static * InsertBlockIndex(uint256 hash)
0474 {
0475     if (hash == 0)
0476         return NULL;
0477 
0478     // Return existing
0479     map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash);
0480     if (mi != mapBlockIndex.end())
0481         return (*mi).second;
0482 
0483     // Create new
0484     CBlockIndex* pindexNew = new CBlockIndex();
0485     if (!pindexNew)
0486         throw runtime_error("LoadBlockIndex() : new CBlockIndex failed");
0487     mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
0488     pindexNew->phashBlock = &((*mi).first);
0489 
0490     return pindexNew;
0491 }
0492 
0493 bool CTxDB::LoadBlockIndex()
0494 {
0495     // Get database cursor
0496     Dbc* pcursor = GetCursor();
0497     if (!pcursor)
0498         return false;
0499 
0500     // Load mapBlockIndex
0501     unsigned int fFlags = DB_SET_RANGE;
0502     loop
0503     {
0504         // Read next record
0505         CDataStream ssKey;
0506         if (fFlags == DB_SET_RANGE)
0507             ssKey << make_pair(string("blockindex"), uint256(0));
0508         CDataStream ssValue;
0509         int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
0510         fFlags = DB_NEXT;
0511         if (ret == DB_NOTFOUND)
0512             break;
0513         else if (ret != 0)
0514             return false;
0515 
0516         // Unserialize
0517         string strType;
0518         ssKey >> strType;
0519         if (strType == "blockindex")
0520         {
0521             CDiskBlockIndex diskindex;
0522             ssValue >> diskindex;
0523 
0524             // Construct block index object
0525             CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash());
0526             pindexNew->pprev          = InsertBlockIndex(diskindex.hashPrev);
0527             pindexNew->pnext          = InsertBlockIndex(diskindex.hashNext);
0528             pindexNew->nFile          = diskindex.nFile;
0529             pindexNew->nBlockPos      = diskindex.nBlockPos;
0530             pindexNew->nHeight        = diskindex.nHeight;
0531             pindexNew->nVersion       = diskindex.nVersion;
0532             pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
0533             pindexNew->nTime          = diskindex.nTime;
0534             pindexNew->nBits          = diskindex.nBits;
0535             pindexNew->nNonce         = diskindex.nNonce;
0536 
0537             // Watch for genesis block
0538             if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == hashGenesisBlock)
0539                 pindexGenesisBlock = pindexNew;
0540 
0541             if (!pindexNew->CheckIndex())
0542                 return error("LoadBlockIndex() : CheckIndex failed at %d", pindexNew->nHeight);
0543         }
0544         else
0545         {
0546             break;
0547         }
0548     }
0549     pcursor->close();
0550 
0551     // Calculate bnChainWork
0552     vector<pair<int, CBlockIndex*> > vSortedByHeight;
0553     vSortedByHeight.reserve(mapBlockIndex.size());
0554     BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex)
0555     {
0556         CBlockIndex* pindex = item.second;
0557         vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex));
0558     }
0559     sort(vSortedByHeight.begin(), vSortedByHeight.end());
0560     BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight)
0561     {
0562         CBlockIndex* pindex = item.second;
0563         pindex->bnChainWork = (pindex->pprev ? pindex->pprev->bnChainWork : 0) + pindex->GetBlockWork();
0564     }
0565 
0566     // Load hashBestChain pointer to end of best chain
0567     if (!ReadHashBestChain(hashBestChain))
0568     {
0569         if (pindexGenesisBlock == NULL)
0570             return true;
0571         return error("CTxDB::LoadBlockIndex() : hashBestChain not loaded");
0572     }
0573     if (!mapBlockIndex.count(hashBestChain))
0574         return error("CTxDB::LoadBlockIndex() : hashBestChain not found in the block index");
0575     pindexBest = mapBlockIndex[hashBestChain];
0576     nBestHeight = pindexBest->nHeight;
0577     bnBestChainWork = pindexBest->bnChainWork;
0578     printf("LoadBlockIndex(): hashBestChain=%s  height=%d\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight);
0579 
0580     // Load bnBestInvalidWork, OK if it doesn't exist
0581     ReadBestInvalidWork(bnBestInvalidWork);
0582 
0583     // Verify blocks in the best chain
0584     CBlockIndex* pindexFork = NULL;
0585     for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev)
0586     {
0587         if (pindex->nHeight < nBestHeight-2500 && !mapArgs.count("-checkblocks"))
0588             break;
0589         CBlock block;
0590         if (!block.ReadFromDisk(pindex))
0591             return error("LoadBlockIndex() : block.ReadFromDisk failed");
0592         if (!block.CheckBlock())
0593         {
0594             printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
0595             pindexFork = pindex->pprev;
0596         }
0597     }
0598     if (pindexFork)
0599     {
0600         // Reorg back to the fork
0601         printf("LoadBlockIndex() : *** moving best chain pointer back to block %d\n", pindexFork->nHeight);
0602         CBlock block;
0603         if (!block.ReadFromDisk(pindexFork))
0604             return error("LoadBlockIndex() : block.ReadFromDisk failed");
0605         CTxDB txdb;
0606         block.SetBestChain(txdb, pindexFork);
0607     }
0608 
0609     return true;
0610 }
0611 
0612 
0613 
0614 
0615 
0616 //
0617 // CAddrDB
0618 //
0619 
0620 bool CAddrDB::WriteAddress(const CAddress& addr)
0621 {
0622     return Write(make_pair(string("addr"), addr.GetKey()), addr);
0623 }
0624 
0625 bool CAddrDB::EraseAddress(const CAddress& addr)
0626 {
0627     return Erase(make_pair(string("addr"), addr.GetKey()));
0628 }
0629 
0630 bool CAddrDB::LoadAddresses()
0631 {
0632     CRITICAL_BLOCK(cs_mapAddresses)
0633     {
0634         // Get cursor
0635         Dbc* pcursor = GetCursor();
0636         if (!pcursor)
0637             return false;
0638 
0639         loop
0640         {
0641             // Read next record
0642             CDataStream ssKey;
0643             CDataStream ssValue;
0644             int ret = ReadAtCursor(pcursor, ssKey, ssValue);
0645             if (ret == DB_NOTFOUND)
0646                 break;
0647             else if (ret != 0)
0648                 return false;
0649 
0650             // Unserialize
0651             string strType;
0652             ssKey >> strType;
0653             if (strType == "addr")
0654             {
0655                 CAddress addr;
0656                 ssValue >> addr;
0657                 mapAddresses.insert(make_pair(addr.GetKey(), addr));
0658             }
0659         }
0660         pcursor->close();
0661 
0662         printf("Loaded %d addresses\n", mapAddresses.size());
0663     }
0664 
0665     return true;
0666 }
0667 
0668 bool LoadAddresses()
0669 {
0670     return CAddrDB("cr+").LoadAddresses();
0671 }
0672 
0673 
0674 
0675 
0676 //
0677 // CWalletDB
0678 //
0679 
0680 bool CWalletDB::WriteName(const string& strAddress, const string& strName)
0681 {
0682     nWalletDBUpdated++;
0683     return Write(make_pair(string("name"), strAddress), strName);
0684 }
0685 
0686 bool CWalletDB::EraseName(const string& strAddress)
0687 {
0688     // This should only be used for sending addresses, never for receiving addresses,
0689     // receiving addresses must always have an address book entry if they're not change return.
0690     nWalletDBUpdated++;
0691     return Erase(make_pair(string("name"), strAddress));
0692 }
0693 
0694 bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account)
0695 {
0696     account.SetNull();
0697     return Read(make_pair(string("acc"), strAccount), account);
0698 }
0699 
0700 bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account)
0701 {
0702     return Write(make_pair(string("acc"), strAccount), account);
0703 }
0704 
0705 bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry)
0706 {
0707     return Write(boost::make_tuple(string("acentry"), acentry.strAccount, ++nAccountingEntryNumber), acentry);
0708 }
0709 
0710 int64 CWalletDB::GetAccountCreditDebit(const string& strAccount)
0711 {
0712     list<CAccountingEntry> entries;
0713     ListAccountCreditDebit(strAccount, entries);
0714 
0715     int64 nCreditDebit = 0;
0716     BOOST_FOREACH (const CAccountingEntry& entry, entries)
0717         nCreditDebit += entry.nCreditDebit;
0718 
0719     return nCreditDebit;
0720 }
0721 
0722 void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountingEntry>& entries)
0723 {
0724     bool fAllAccounts = (strAccount == "*");
0725 
0726     Dbc* pcursor = GetCursor();
0727     if (!pcursor)
0728         throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor");
0729     unsigned int fFlags = DB_SET_RANGE;
0730     loop
0731     {
0732         // Read next record
0733         CDataStream ssKey;
0734         if (fFlags == DB_SET_RANGE)
0735             ssKey << boost::make_tuple(string("acentry"), (fAllAccounts? string("") : strAccount), uint64(0));
0736         CDataStream ssValue;
0737         int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
0738         fFlags = DB_NEXT;
0739         if (ret == DB_NOTFOUND)
0740             break;
0741         else if (ret != 0)
0742         {
0743             pcursor->close();
0744             throw runtime_error("CWalletDB::ListAccountCreditDebit() : error scanning DB");
0745         }
0746 
0747         // Unserialize
0748         string strType;
0749         ssKey >> strType;
0750         if (strType != "acentry")
0751             break;
0752         CAccountingEntry acentry;
0753         ssKey >> acentry.strAccount;
0754         if (!fAllAccounts && acentry.strAccount != strAccount)
0755             break;
0756 
0757         ssValue >> acentry;
0758         entries.push_back(acentry);
0759     }
0760 
0761     pcursor->close();
0762 }
0763 
0764 
0765 int CWalletDB::LoadWallet(CWallet* pwallet)
0766 {
0767     pwallet->vchDefaultKey.clear();
0768     int nFileVersion = 0;
0769     vector<uint256> vWalletUpgrade;
0770     bool fIsEncrypted = false;
0771 
0772     // Modify defaults
0773     // Tray icon sometimes disappears on 9.10 karmic koala 64-bit, leaving no way to access the program
0774     fMinimizeToTray = false;
0775     fMinimizeOnClose = false;
0776 
0777     //// todo: shouldn't we catch exceptions and try to recover and continue?
0778     CRITICAL_BLOCK(pwallet->cs_wallet)
0779     {
0780         // Get cursor
0781         Dbc* pcursor = GetCursor();
0782         if (!pcursor)
0783             return DB_CORRUPT;
0784 
0785         loop
0786         {
0787             // Read next record
0788             CDataStream ssKey;
0789             CDataStream ssValue;
0790             int ret = ReadAtCursor(pcursor, ssKey, ssValue);
0791             if (ret == DB_NOTFOUND)
0792                 break;
0793             else if (ret != 0)
0794                 return DB_CORRUPT;
0795 
0796             // Unserialize
0797             // Taking advantage of the fact that pair serialization
0798             // is just the two items serialized one after the other
0799             string strType;
0800             ssKey >> strType;
0801             if (strType == "name")
0802             {
0803                 string strAddress;
0804                 ssKey >> strAddress;
0805                 ssValue >> pwallet->mapAddressBook[strAddress];
0806             }
0807             else if (strType == "tx")
0808             {
0809                 uint256 hash;
0810                 ssKey >> hash;
0811                 CWalletTx& wtx = pwallet->mapWallet[hash];
0812                 ssValue >> wtx;
0813                 wtx.pwallet = pwallet;
0814 
0815                 if (wtx.GetHash() != hash)
0816                     printf("Error in wallet.dat, hash mismatch\n");
0817 
0818                 // Undo serialize changes in 31600
0819                 if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
0820                 {
0821                     if (!ssValue.empty())
0822                     {
0823                         char fTmp;
0824                         char fUnused;
0825                         ssValue >> fTmp >> fUnused >> wtx.strFromAccount;
0826                         printf("LoadWallet() upgrading tx ver=%d %d '%s' %s\n", wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str());
0827                         wtx.fTimeReceivedIsTxTime = fTmp;
0828                     }
0829                     else
0830                     {
0831                         printf("LoadWallet() repairing tx ver=%d %s\n", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str());
0832                         wtx.fTimeReceivedIsTxTime = 0;
0833                     }
0834                     vWalletUpgrade.push_back(hash);
0835                 }
0836 
0837                 //// debug print
0838                 //printf("LoadWallet  %s\n", wtx.GetHash().ToString().c_str());
0839                 //printf(" %12I64d  %s  %s  %s\n",
0840                 //    wtx.vout[0].nValue,
0841                 //    DateTimeStrFormat("%x %H:%M:%S", wtx.GetBlockTime()).c_str(),
0842                 //    wtx.hashBlock.ToString().substr(0,20).c_str(),
0843                 //    wtx.mapValue["message"].c_str());
0844             }
0845             else if (strType == "acentry")
0846             {
0847                 string strAccount;
0848                 ssKey >> strAccount;
0849                 uint64 nNumber;
0850                 ssKey >> nNumber;
0851                 if (nNumber > nAccountingEntryNumber)
0852                     nAccountingEntryNumber = nNumber;
0853             }
0854             else if (strType == "key" || strType == "wkey")
0855             {
0856                 vector<unsigned char> vchPubKey;
0857                 ssKey >> vchPubKey;
0858                 CKey key;
0859                 if (strType == "key")
0860                 {
0861                     CPrivKey pkey;
0862                     ssValue >> pkey;
0863                     key.SetPrivKey(pkey);
0864                     if (key.GetPubKey() != vchPubKey || !key.IsValid())
0865                         return DB_CORRUPT;
0866                 }
0867                 else
0868                 {
0869                     CWalletKey wkey;
0870                     ssValue >> wkey;
0871                     key.SetPrivKey(wkey.vchPrivKey);
0872                     if (key.GetPubKey() != vchPubKey || !key.IsValid())
0873                         return DB_CORRUPT;
0874                 }
0875                 if (!pwallet->LoadKey(key))
0876                     return DB_CORRUPT;
0877             }
0878             else if (strType == "mkey")
0879             {
0880                 unsigned int nID;
0881                 ssKey >> nID;
0882                 CMasterKey kMasterKey;
0883                 ssValue >> kMasterKey;
0884                 if(pwallet->mapMasterKeys.count(nID) != 0)
0885                     return DB_CORRUPT;
0886                 pwallet->mapMasterKeys[nID] = kMasterKey;
0887                 if (pwallet->nMasterKeyMaxID < nID)
0888                     pwallet->nMasterKeyMaxID = nID;
0889             }
0890             else if (strType == "ckey")
0891             {
0892                 vector<unsigned char> vchPubKey;
0893                 ssKey >> vchPubKey;
0894                 vector<unsigned char> vchPrivKey;
0895                 ssValue >> vchPrivKey;
0896                 if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey))
0897                     return DB_CORRUPT;
0898                 fIsEncrypted = true;
0899             }
0900             else if (strType == "defaultkey")
0901             {
0902                 ssValue >> pwallet->vchDefaultKey;
0903             }
0904             else if (strType == "pool")
0905             {
0906                 int64 nIndex;
0907                 ssKey >> nIndex;
0908                 pwallet->setKeyPool.insert(nIndex);
0909             }
0910             else if (strType == "version")
0911             {
0912                 ssValue >> nFileVersion;
0913                 if (nFileVersion == 10300)
0914                     nFileVersion = 300;
0915             }
0916             else if (strType == "setting")
0917             {
0918                 string strKey;
0919                 ssKey >> strKey;
0920 
0921                 // Options
0922                 if (strKey == "fGenerateBitcoins")  ssValue >> fGenerateBitcoins;
0923                 if (strKey == "nTransactionFee")    ssValue >> nTransactionFee;
0924                 if (strKey == "fLimitProcessors")   ssValue >> fLimitProcessors;
0925                 if (strKey == "nLimitProcessors")   ssValue >> nLimitProcessors;
0926                 if (strKey == "fMinimizeToTray")    ssValue >> fMinimizeToTray;
0927                 if (strKey == "fMinimizeOnClose")   ssValue >> fMinimizeOnClose;
0928                 if (strKey == "fUseProxy")          ssValue >> fUseProxy;
0929                 if (strKey == "addrProxy")          ssValue >> addrProxy;
0930             }
0931             else if (strType == "minversion")
0932             {
0933                 int nMinVersion = 0;
0934                 ssValue >> nMinVersion;
0935                 if (nMinVersion > VERSION)
0936                     return DB_TOO_NEW;
0937             }
0938         }
0939         pcursor->close();
0940     }
0941 
0942     BOOST_FOREACH(uint256 hash, vWalletUpgrade)
0943         WriteTx(hash, pwallet->mapWallet[hash]);
0944 
0945     printf("nFileVersion = %d\n", nFileVersion);
0946     printf("fGenerateBitcoins = %d\n", fGenerateBitcoins);
0947     printf("nTransactionFee = %"PRI64d"\n", nTransactionFee);
0948     printf("fMinimizeToTray = %d\n", fMinimizeToTray);
0949     printf("fMinimizeOnClose = %d\n", fMinimizeOnClose);
0950     printf("fUseProxy = %d\n", fUseProxy);
0951     printf("addrProxy = %s\n", addrProxy.ToString().c_str());
0952 
0953     // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
0954     if (fIsEncrypted && (nFileVersion == 40000 || nFileVersion == 50000))
0955         return DB_NEED_REWRITE;
0956 
0957     if (nFileVersion < VERSION) // Update
0958     {
0959         // Get rid of old debug.log file in current directory
0960         if (nFileVersion <= 105 && !pszSetDataDir[0])
0961             unlink("debug.log");
0962 
0963         WriteVersion(VERSION);
0964     }
0965 
0966     return DB_LOAD_OK;
0967 }
0968 
0969 void ThreadFlushWalletDB(void* parg)
0970 {
0971     const string& strFile = ((const string*)parg)[0];
0972     static bool fOneThread;
0973     if (fOneThread)
0974         return;
0975     fOneThread = true;
0976     if (mapArgs.count("-noflushwallet"))
0977         return;
0978 
0979     unsigned int nLastSeen = nWalletDBUpdated;
0980     unsigned int nLastFlushed = nWalletDBUpdated;
0981     int64 nLastWalletUpdate = GetTime();
0982     while (!fShutdown)
0983     {
0984         Sleep(500);
0985 
0986         if (nLastSeen != nWalletDBUpdated)
0987         {
0988             nLastSeen = nWalletDBUpdated;
0989             nLastWalletUpdate = GetTime();
0990         }
0991 
0992         if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2)
0993         {
0994             TRY_CRITICAL_BLOCK(cs_db)
0995             {
0996                 // Don't do this if any databases are in use
0997                 int nRefCount = 0;
0998                 map<string, int>::iterator mi = mapFileUseCount.begin();
0999                 while (mi != mapFileUseCount.end())
1000                 {
1001                     nRefCount += (*mi).second;
1002                     mi++;
1003                 }
1004 
1005                 if (nRefCount == 0 && !fShutdown)
1006                 {
1007                     map<string, int>::iterator mi = mapFileUseCount.find(strFile);
1008                     if (mi != mapFileUseCount.end())
1009                     {
1010                         printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str());
1011                         printf("Flushing wallet.dat\n");
1012                         nLastFlushed = nWalletDBUpdated;
1013                         int64 nStart = GetTimeMillis();
1014 
1015                         // Flush wallet.dat so it's self contained
1016                         CloseDb(strFile);
1017                         dbenv.txn_checkpoint(0, 0, 0);
1018                         dbenv.lsn_reset(strFile.c_str(), 0);
1019 
1020                         mapFileUseCount.erase(mi++);
1021                         printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart);
1022                     }
1023                 }
1024             }
1025         }
1026     }
1027 }
1028 
1029 bool BackupWallet(const CWallet& wallet, const string& strDest)
1030 {
1031     if (!wallet.fFileBacked)
1032         return false;
1033     while (!fShutdown)
1034     {
1035         CRITICAL_BLOCK(cs_db)
1036         {
1037             if (!mapFileUseCount.count(wallet.strWalletFile) || mapFileUseCount[wallet.strWalletFile] == 0)
1038             {
1039                 // Flush log data to the dat file
1040                 CloseDb(wallet.strWalletFile);
1041                 dbenv.txn_checkpoint(0, 0, 0);
1042                 dbenv.lsn_reset(wallet.strWalletFile.c_str(), 0);
1043                 mapFileUseCount.erase(wallet.strWalletFile);
1044 
1045                 // Copy wallet.dat
1046                 filesystem::path pathSrc(GetDataDir() + "/" + wallet.strWalletFile);
1047                 filesystem::path pathDest(strDest);
1048                 if (filesystem::is_directory(pathDest))
1049                     pathDest = pathDest / wallet.strWalletFile;
1050 #if BOOST_VERSION >= 104000
1051                 filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists);
1052 #else
1053                 filesystem::copy_file(pathSrc, pathDest);
1054 #endif
1055                 printf("copied wallet.dat to %s\n", pathDest.string().c_str());
1056 
1057                 return true;
1058             }
1059         }
1060         Sleep(100);
1061     }
1062     return false;
1063 }