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