LCOV - code coverage report
Current view: top level - src/wallet - walletdb.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 283 401 70.6 %
Date: 2015-10-12 22:39:14 Functions: 32 41 78.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2009-2010 Satoshi Nakamoto
       2             : // Copyright (c) 2009-2014 The Bitcoin Core developers
       3             : // Distributed under the MIT software license, see the accompanying
       4             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       5             : 
       6             : #include "wallet/walletdb.h"
       7             : 
       8             : #include "base58.h"
       9             : #include "consensus/validation.h"
      10             : #include "main.h" // For CheckTransaction
      11             : #include "protocol.h"
      12             : #include "serialize.h"
      13             : #include "sync.h"
      14             : #include "util.h"
      15             : #include "utiltime.h"
      16             : #include "wallet/wallet.h"
      17             : 
      18             : #include <boost/filesystem.hpp>
      19             : #include <boost/foreach.hpp>
      20             : #include <boost/scoped_ptr.hpp>
      21             : #include <boost/thread.hpp>
      22             : 
      23             : using namespace std;
      24             : 
      25             : static uint64_t nAccountingEntryNumber = 0;
      26             : 
      27             : //
      28             : // CWalletDB
      29             : //
      30             : 
      31         249 : bool CWalletDB::WriteName(const string& strAddress, const string& strName)
      32             : {
      33         249 :     nWalletDBUpdated++;
      34        1245 :     return Write(make_pair(string("name"), strAddress), strName);
      35             : }
      36             : 
      37           0 : bool CWalletDB::EraseName(const string& strAddress)
      38             : {
      39             :     // This should only be used for sending addresses, never for receiving addresses,
      40             :     // receiving addresses must always have an address book entry if they're not change return.
      41           0 :     nWalletDBUpdated++;
      42           0 :     return Erase(make_pair(string("name"), strAddress));
      43             : }
      44             : 
      45         249 : bool CWalletDB::WritePurpose(const string& strAddress, const string& strPurpose)
      46             : {
      47         249 :     nWalletDBUpdated++;
      48        1245 :     return Write(make_pair(string("purpose"), strAddress), strPurpose);
      49             : }
      50             : 
      51           0 : bool CWalletDB::ErasePurpose(const string& strPurpose)
      52             : {
      53           0 :     nWalletDBUpdated++;
      54           0 :     return Erase(make_pair(string("purpose"), strPurpose));
      55             : }
      56             : 
      57        1889 : bool CWalletDB::WriteTx(uint256 hash, const CWalletTx& wtx)
      58             : {
      59        1889 :     nWalletDBUpdated++;
      60        9445 :     return Write(std::make_pair(std::string("tx"), hash), wtx);
      61             : }
      62             : 
      63           6 : bool CWalletDB::EraseTx(uint256 hash)
      64             : {
      65           6 :     nWalletDBUpdated++;
      66          30 :     return Erase(std::make_pair(std::string("tx"), hash));
      67             : }
      68             : 
      69        1349 : bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta)
      70             : {
      71        1349 :     nWalletDBUpdated++;
      72             : 
      73        2698 :     if (!Write(std::make_pair(std::string("keymeta"), vchPubKey),
      74        6745 :                keyMeta, false))
      75             :         return false;
      76             : 
      77             :     // hash pubkey/privkey to accelerate wallet load
      78             :     std::vector<unsigned char> vchKey;
      79        4047 :     vchKey.reserve(vchPubKey.size() + vchPrivKey.size());
      80        2698 :     vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
      81        4047 :     vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end());
      82             : 
      83       10792 :     return Write(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false);
      84             : }
      85             : 
      86          65 : bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey,
      87             :                                 const std::vector<unsigned char>& vchCryptedSecret,
      88             :                                 const CKeyMetadata &keyMeta)
      89             : {
      90          65 :     const bool fEraseUnencryptedKey = true;
      91          65 :     nWalletDBUpdated++;
      92             : 
      93         130 :     if (!Write(std::make_pair(std::string("keymeta"), vchPubKey),
      94         325 :             keyMeta))
      95             :         return false;
      96             : 
      97         325 :     if (!Write(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false))
      98             :         return false;
      99             :     if (fEraseUnencryptedKey)
     100             :     {
     101         325 :         Erase(std::make_pair(std::string("key"), vchPubKey));
     102         325 :         Erase(std::make_pair(std::string("wkey"), vchPubKey));
     103             :     }
     104          65 :     return true;
     105             : }
     106             : 
     107           1 : bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey)
     108             : {
     109           1 :     nWalletDBUpdated++;
     110           5 :     return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true);
     111             : }
     112             : 
     113           9 : bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript)
     114             : {
     115           9 :     nWalletDBUpdated++;
     116          45 :     return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false);
     117             : }
     118             : 
     119           4 : bool CWalletDB::WriteWatchOnly(const CScript &dest)
     120             : {
     121           4 :     nWalletDBUpdated++;
     122          24 :     return Write(std::make_pair(std::string("watchs"), dest), '1');
     123             : }
     124             : 
     125           0 : bool CWalletDB::EraseWatchOnly(const CScript &dest)
     126             : {
     127           0 :     nWalletDBUpdated++;
     128           0 :     return Erase(std::make_pair(std::string("watchs"), dest));
     129             : }
     130             : 
     131          87 : bool CWalletDB::WriteBestBlock(const CBlockLocator& locator)
     132             : {
     133          87 :     nWalletDBUpdated++;
     134         348 :     Write(std::string("bestblock"), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan
     135         261 :     return Write(std::string("bestblock_nomerkle"), locator);
     136             : }
     137             : 
     138          93 : bool CWalletDB::ReadBestBlock(CBlockLocator& locator)
     139             : {
     140         372 :     if (Read(std::string("bestblock"), locator) && !locator.vHave.empty()) return true;
     141         279 :     return Read(std::string("bestblock_nomerkle"), locator);
     142             : }
     143             : 
     144        1627 : bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext)
     145             : {
     146        1627 :     nWalletDBUpdated++;
     147        4881 :     return Write(std::string("orderposnext"), nOrderPosNext);
     148             : }
     149             : 
     150          37 : bool CWalletDB::WriteDefaultKey(const CPubKey& vchPubKey)
     151             : {
     152          37 :     nWalletDBUpdated++;
     153         111 :     return Write(std::string("defaultkey"), vchPubKey);
     154             : }
     155             : 
     156         937 : bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool)
     157             : {
     158        4685 :     return Read(std::make_pair(std::string("pool"), nPool), keypool);
     159             : }
     160             : 
     161        1054 : bool CWalletDB::WritePool(int64_t nPool, const CKeyPool& keypool)
     162             : {
     163        1054 :     nWalletDBUpdated++;
     164        5270 :     return Write(std::make_pair(std::string("pool"), nPool), keypool);
     165             : }
     166             : 
     167         616 : bool CWalletDB::ErasePool(int64_t nPool)
     168             : {
     169         616 :     nWalletDBUpdated++;
     170        3080 :     return Erase(std::make_pair(std::string("pool"), nPool));
     171             : }
     172             : 
     173          37 : bool CWalletDB::WriteMinVersion(int nVersion)
     174             : {
     175         111 :     return Write(std::string("minversion"), nVersion);
     176             : }
     177             : 
     178           5 : bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account)
     179             : {
     180             :     account.SetNull();
     181          25 :     return Read(make_pair(string("acc"), strAccount), account);
     182             : }
     183             : 
     184           5 : bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account)
     185             : {
     186          25 :     return Write(make_pair(string("acc"), strAccount), account);
     187             : }
     188             : 
     189          10 : bool CWalletDB::WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry)
     190             : {
     191          70 :     return Write(std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry);
     192             : }
     193             : 
     194           4 : bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry)
     195             : {
     196           4 :     return WriteAccountingEntry(++nAccountingEntryNumber, acentry);
     197             : }
     198             : 
     199          30 : CAmount CWalletDB::GetAccountCreditDebit(const string& strAccount)
     200             : {
     201             :     list<CAccountingEntry> entries;
     202          30 :     ListAccountCreditDebit(strAccount, entries);
     203             : 
     204          30 :     CAmount nCreditDebit = 0;
     205         180 :     BOOST_FOREACH (const CAccountingEntry& entry, entries)
     206           0 :         nCreditDebit += entry.nCreditDebit;
     207             : 
     208          60 :     return nCreditDebit;
     209             : }
     210             : 
     211        1418 : void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountingEntry>& entries)
     212             : {
     213        1418 :     bool fAllAccounts = (strAccount == "*");
     214             : 
     215        1418 :     Dbc* pcursor = GetCursor();
     216        1418 :     if (!pcursor)
     217           0 :         throw runtime_error("CWalletDB::ListAccountCreditDebit(): cannot create DB cursor");
     218             :     unsigned int fFlags = DB_SET_RANGE;
     219             :     while (true)
     220             :     {
     221             :         // Read next record
     222             :         CDataStream ssKey(SER_DISK, CLIENT_VERSION);
     223        1442 :         if (fFlags == DB_SET_RANGE)
     224        9942 :             ssKey << std::make_pair(std::string("acentry"), std::make_pair((fAllAccounts ? string("") : strAccount), uint64_t(0)));
     225             :         CDataStream ssValue(SER_DISK, CLIENT_VERSION);
     226        1442 :         int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
     227        1442 :         fFlags = DB_NEXT;
     228        1442 :         if (ret == DB_NOTFOUND)
     229             :             break;
     230        1442 :         else if (ret != 0)
     231             :         {
     232           0 :             pcursor->close();
     233           0 :             throw runtime_error("CWalletDB::ListAccountCreditDebit(): error scanning DB");
     234             :         }
     235             : 
     236             :         // Unserialize
     237             :         string strType;
     238             :         ssKey >> strType;
     239        1442 :         if (strType != "acentry")
     240             :             break;
     241          48 :         CAccountingEntry acentry;
     242             :         ssKey >> acentry.strAccount;
     243          48 :         if (!fAllAccounts && acentry.strAccount != strAccount)
     244             :             break;
     245             : 
     246             :         ssValue >> acentry;
     247             :         ssKey >> acentry.nEntryNo;
     248             :         entries.push_back(acentry);
     249             :     }
     250             : 
     251        1442 :     pcursor->close();
     252        1418 : }
     253             : 
     254           4 : DBErrors CWalletDB::ReorderTransactions(CWallet* pwallet)
     255             : {
     256           4 :     LOCK(pwallet->cs_wallet);
     257             :     // Old wallets didn't have any defined order for transactions
     258             :     // Probably a bad idea to change the output of this
     259             : 
     260             :     // First: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap.
     261             :     typedef pair<CWalletTx*, CAccountingEntry*> TxPair;
     262             :     typedef multimap<int64_t, TxPair > TxItems;
     263             :     TxItems txByTime;
     264             : 
     265          32 :     for (map<uint256, CWalletTx>::iterator it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it)
     266             :     {
     267           8 :         CWalletTx* wtx = &((*it).second);
     268          16 :         txByTime.insert(make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0)));
     269             :     }
     270             :     list<CAccountingEntry> acentries;
     271          12 :     ListAccountCreditDebit("", acentries);
     272          84 :     BOOST_FOREACH(CAccountingEntry& entry, acentries)
     273             :     {
     274          24 :         txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry)));
     275             :     }
     276             : 
     277           4 :     int64_t& nOrderPosNext = pwallet->nOrderPosNext;
     278           4 :     nOrderPosNext = 0;
     279             :     std::vector<int64_t> nOrderPosOffsets;
     280          28 :     for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it)
     281             :     {
     282          20 :         CWalletTx *const pwtx = (*it).second.first;
     283          20 :         CAccountingEntry *const pacentry = (*it).second.second;
     284          20 :         int64_t& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos;
     285             : 
     286          20 :         if (nOrderPos == -1)
     287             :         {
     288           5 :             nOrderPos = nOrderPosNext++;
     289           5 :             nOrderPosOffsets.push_back(nOrderPos);
     290             : 
     291           5 :             if (pwtx)
     292             :             {
     293           4 :                 if (!WriteTx(pwtx->GetHash(), *pwtx))
     294             :                     return DB_LOAD_FAIL;
     295             :             }
     296             :             else
     297           3 :                 if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry))
     298             :                     return DB_LOAD_FAIL;
     299             :         }
     300             :         else
     301             :         {
     302          15 :             int64_t nOrderPosOff = 0;
     303         138 :             BOOST_FOREACH(const int64_t& nOffsetStart, nOrderPosOffsets)
     304             :             {
     305           8 :                 if (nOrderPos >= nOffsetStart)
     306           6 :                     ++nOrderPosOff;
     307             :             }
     308          15 :             nOrderPos += nOrderPosOff;
     309          30 :             nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1);
     310             : 
     311          15 :             if (!nOrderPosOff)
     312             :                 continue;
     313             : 
     314             :             // Since we're changing the order, write it back
     315           6 :             if (pwtx)
     316             :             {
     317           6 :                 if (!WriteTx(pwtx->GetHash(), *pwtx))
     318             :                     return DB_LOAD_FAIL;
     319             :             }
     320             :             else
     321           3 :                 if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry))
     322             :                     return DB_LOAD_FAIL;
     323             :         }
     324             :     }
     325           4 :     WriteOrderPosNext(nOrderPosNext);
     326             : 
     327             :     return DB_LOAD_OK;
     328             : }
     329             : 
     330         238 : class CWalletScanState {
     331             : public:
     332             :     unsigned int nKeys;
     333             :     unsigned int nCKeys;
     334             :     unsigned int nKeyMeta;
     335             :     bool fIsEncrypted;
     336             :     bool fAnyUnordered;
     337             :     int nFileVersion;
     338             :     vector<uint256> vWalletUpgrade;
     339             : 
     340         119 :     CWalletScanState() {
     341         119 :         nKeys = nCKeys = nKeyMeta = 0;
     342         119 :         fIsEncrypted = false;
     343         119 :         fAnyUnordered = false;
     344         119 :         nFileVersion = 0;
     345           0 :     }
     346             : };
     347             : 
     348             : bool
     349        9005 : ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
     350             :              CWalletScanState &wss, string& strType, string& strErr)
     351             : {
     352             :     try {
     353             :         // Unserialize
     354             :         // Taking advantage of the fact that pair serialization
     355             :         // is just the two items serialized one after the other
     356             :         ssKey >> strType;
     357        9005 :         if (strType == "name")
     358             :         {
     359             :             string strAddress;
     360             :             ssKey >> strAddress;
     361         393 :             ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].name;
     362             :         }
     363        8874 :         else if (strType == "purpose")
     364             :         {
     365             :             string strAddress;
     366             :             ssKey >> strAddress;
     367         393 :             ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].purpose;
     368             :         }
     369        8743 :         else if (strType == "tx")
     370             :         {
     371             :             uint256 hash;
     372             :             ssKey >> hash;
     373        2738 :             CWalletTx wtx;
     374             :             ssValue >> wtx;
     375        2738 :             CValidationState state;
     376        5476 :             if (!(CheckTransaction(wtx, state) && (wtx.GetHash() == hash) && state.IsValid()))
     377           0 :                 return false;
     378             : 
     379             :             // Undo serialize changes in 31600
     380        2738 :             if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
     381             :             {
     382           0 :                 if (!ssValue.empty())
     383             :                 {
     384             :                     char fTmp;
     385             :                     char fUnused;
     386           0 :                     ssValue >> fTmp >> fUnused >> wtx.strFromAccount;
     387           0 :                     strErr = strprintf("LoadWallet() upgrading tx ver=%d %d '%s' %s",
     388             :                                        wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount, hash.ToString());
     389           0 :                     wtx.fTimeReceivedIsTxTime = fTmp;
     390             :                 }
     391             :                 else
     392             :                 {
     393           0 :                     strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString());
     394           0 :                     wtx.fTimeReceivedIsTxTime = 0;
     395             :                 }
     396           0 :                 wss.vWalletUpgrade.push_back(hash);
     397             :             }
     398             : 
     399        2738 :             if (wtx.nOrderPos == -1)
     400           0 :                 wss.fAnyUnordered = true;
     401             : 
     402        5476 :             pwallet->AddToWallet(wtx, true, NULL);
     403             :         }
     404        6005 :         else if (strType == "acentry")
     405             :         {
     406             :             string strAccount;
     407             :             ssKey >> strAccount;
     408             :             uint64_t nNumber;
     409             :             ssKey >> nNumber;
     410           0 :             if (nNumber > nAccountingEntryNumber)
     411           0 :                 nAccountingEntryNumber = nNumber;
     412             : 
     413           0 :             if (!wss.fAnyUnordered)
     414             :             {
     415           0 :                 CAccountingEntry acentry;
     416             :                 ssValue >> acentry;
     417           0 :                 if (acentry.nOrderPos == -1)
     418           0 :                     wss.fAnyUnordered = true;
     419             :             }
     420             :         }
     421        6005 :         else if (strType == "watchs")
     422             :         {
     423             :             CScript script;
     424             :             ssKey >> script;
     425             :             char fYes;
     426             :             ssValue >> fYes;
     427           2 :             if (fYes == '1')
     428           2 :                 pwallet->LoadWatchOnly(script);
     429             : 
     430             :             // Watch-only addresses have no birthday information for now,
     431             :             // so set the wallet birthday to the beginning of time.
     432           2 :             pwallet->nTimeFirstKey = 1;
     433             :         }
     434        9408 :         else if (strType == "key" || strType == "wkey")
     435             :         {
     436             :             CPubKey vchPubKey;
     437             :             ssKey >> vchPubKey;
     438        2598 :             if (!vchPubKey.IsValid())
     439             :             {
     440             :                 strErr = "Error reading wallet database: CPubKey corrupt";
     441           0 :                 return false;
     442             :             }
     443             :             CKey key;
     444             :             CPrivKey pkey;
     445             :             uint256 hash;
     446             : 
     447        2598 :             if (strType == "key")
     448             :             {
     449        2598 :                 wss.nKeys++;
     450             :                 ssValue >> pkey;
     451             :             } else {
     452           0 :                 CWalletKey wkey;
     453             :                 ssValue >> wkey;
     454           0 :                 pkey = wkey.vchPrivKey;
     455             :             }
     456             : 
     457             :             // Old wallets store keys as "key" [pubkey] => [privkey]
     458             :             // ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key
     459             :             // using EC operations as a checksum.
     460             :             // Newer wallets store keys as "key"[pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while
     461             :             // remaining backwards-compatible.
     462             :             try
     463             :             {
     464             :                 ssValue >> hash;
     465             :             }
     466           0 :             catch (...) {}
     467             : 
     468        2598 :             bool fSkipCheck = false;
     469             : 
     470        2598 :             if (!hash.IsNull())
     471             :             {
     472             :                 // hash pubkey/privkey to accelerate wallet load
     473             :                 std::vector<unsigned char> vchKey;
     474        7794 :                 vchKey.reserve(vchPubKey.size() + pkey.size());
     475        2598 :                 vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
     476        2598 :                 vchKey.insert(vchKey.end(), pkey.begin(), pkey.end());
     477             : 
     478        5196 :                 if (Hash(vchKey.begin(), vchKey.end()) != hash)
     479             :                 {
     480             :                     strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt";
     481           0 :                     return false;
     482             :                 }
     483             : 
     484        2598 :                 fSkipCheck = true;
     485             :             }
     486             : 
     487        2598 :             if (!key.Load(pkey, vchPubKey, fSkipCheck))
     488             :             {
     489             :                 strErr = "Error reading wallet database: CPrivKey corrupt";
     490             :                 return false;
     491             :             }
     492        2598 :             if (!pwallet->LoadKey(key, vchPubKey))
     493             :             {
     494             :                 strErr = "Error reading wallet database: LoadKey failed";
     495             :                 return false;
     496             :             }
     497             :         }
     498        3405 :         else if (strType == "mkey")
     499             :         {
     500             :             unsigned int nID;
     501             :             ssKey >> nID;
     502           1 :             CMasterKey kMasterKey;
     503             :             ssValue >> kMasterKey;
     504           2 :             if(pwallet->mapMasterKeys.count(nID) != 0)
     505             :             {
     506           0 :                 strErr = strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID);
     507           0 :                 return false;
     508             :             }
     509           1 :             pwallet->mapMasterKeys[nID] = kMasterKey;
     510           1 :             if (pwallet->nMasterKeyMaxID < nID)
     511           1 :                 pwallet->nMasterKeyMaxID = nID;
     512             :         }
     513        3404 :         else if (strType == "ckey")
     514             :         {
     515             :             vector<unsigned char> vchPubKey;
     516             :             ssKey >> vchPubKey;
     517             :             vector<unsigned char> vchPrivKey;
     518             :             ssValue >> vchPrivKey;
     519          22 :             wss.nCKeys++;
     520             : 
     521          22 :             if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey))
     522             :             {
     523             :                 strErr = "Error reading wallet database: LoadCryptedKey failed";
     524           0 :                 return false;
     525             :             }
     526          22 :             wss.fIsEncrypted = true;
     527             :         }
     528        3382 :         else if (strType == "keymeta")
     529             :         {
     530             :             CPubKey vchPubKey;
     531             :             ssKey >> vchPubKey;
     532             :             CKeyMetadata keyMeta;
     533             :             ssValue >> keyMeta;
     534        2620 :             wss.nKeyMeta++;
     535             : 
     536        2620 :             pwallet->LoadKeyMetadata(vchPubKey, keyMeta);
     537             : 
     538             :             // find earliest key creation time, as wallet birthday
     539        5240 :             if (!pwallet->nTimeFirstKey ||
     540        2620 :                 (keyMeta.nCreateTime < pwallet->nTimeFirstKey))
     541           0 :                 pwallet->nTimeFirstKey = keyMeta.nCreateTime;
     542             :         }
     543         762 :         else if (strType == "defaultkey")
     544             :         {
     545          57 :             ssValue >> pwallet->vchDefaultKey;
     546             :         }
     547         705 :         else if (strType == "pool")
     548             :         {
     549             :             int64_t nIndex;
     550             :             ssKey >> nIndex;
     551         355 :             CKeyPool keypool;
     552             :             ssValue >> keypool;
     553         355 :             pwallet->setKeyPool.insert(nIndex);
     554             : 
     555             :             // If no metadata exists yet, create a default with the pool key's
     556             :             // creation time. Note that this may be overwritten by actually
     557             :             // stored metadata for that key later, which is fine.
     558         355 :             CKeyID keyid = keypool.vchPubKey.GetID();
     559         710 :             if (pwallet->mapKeyMetadata.count(keyid) == 0)
     560         355 :                 pwallet->mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime);
     561             :         }
     562         350 :         else if (strType == "version")
     563             :         {
     564         119 :             ssValue >> wss.nFileVersion;
     565         119 :             if (wss.nFileVersion == 10300)
     566           0 :                 wss.nFileVersion = 300;
     567             :         }
     568         231 :         else if (strType == "cscript")
     569             :         {
     570             :             uint160 hash;
     571             :             ssKey >> hash;
     572             :             CScript script;
     573             :             ssValue >> script;
     574           3 :             if (!pwallet->LoadCScript(script))
     575             :             {
     576             :                 strErr = "Error reading wallet database: LoadCScript failed";
     577           0 :                 return false;
     578             :             }
     579             :         }
     580         228 :         else if (strType == "orderposnext")
     581             :         {
     582          57 :             ssValue >> pwallet->nOrderPosNext;
     583             :         }
     584         171 :         else if (strType == "destdata")
     585             :         {
     586             :             std::string strAddress, strKey, strValue;
     587             :             ssKey >> strAddress;
     588             :             ssKey >> strKey;
     589             :             ssValue >> strValue;
     590           0 :             if (!pwallet->LoadDestData(CBitcoinAddress(strAddress).Get(), strKey, strValue))
     591             :             {
     592             :                 strErr = "Error reading wallet database: LoadDestData failed";
     593           0 :                 return false;
     594             :             }
     595             :         }
     596           0 :     } catch (...)
     597             :     {
     598             :         return false;
     599             :     }
     600             :     return true;
     601             : }
     602             : 
     603           0 : static bool IsKeyType(string strType)
     604             : {
     605           0 :     return (strType== "key" || strType == "wkey" ||
     606           0 :             strType == "mkey" || strType == "ckey");
     607             : }
     608             : 
     609         119 : DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
     610             : {
     611         119 :     pwallet->vchDefaultKey = CPubKey();
     612             :     CWalletScanState wss;
     613         119 :     bool fNoncriticalErrors = false;
     614         119 :     DBErrors result = DB_LOAD_OK;
     615             : 
     616             :     try {
     617         119 :         LOCK(pwallet->cs_wallet);
     618         119 :         int nMinVersion = 0;
     619         357 :         if (Read((string)"minversion", nMinVersion))
     620             :         {
     621          57 :             if (nMinVersion > CLIENT_VERSION)
     622             :                 return DB_TOO_NEW;
     623          57 :             pwallet->LoadMinVersion(nMinVersion);
     624             :         }
     625             : 
     626             :         // Get cursor
     627         119 :         Dbc* pcursor = GetCursor();
     628         119 :         if (!pcursor)
     629             :         {
     630           0 :             LogPrintf("Error getting wallet database cursor\n");
     631             :             return DB_CORRUPT;
     632             :         }
     633             : 
     634             :         while (true)
     635             :         {
     636             :             // Read next record
     637             :             CDataStream ssKey(SER_DISK, CLIENT_VERSION);
     638             :             CDataStream ssValue(SER_DISK, CLIENT_VERSION);
     639        9124 :             int ret = ReadAtCursor(pcursor, ssKey, ssValue);
     640        9124 :             if (ret == DB_NOTFOUND)
     641             :                 break;
     642        9005 :             else if (ret != 0)
     643             :             {
     644           0 :                 LogPrintf("Error reading next record from wallet database\n");
     645           0 :                 return DB_CORRUPT;
     646             :             }
     647             : 
     648             :             // Try to be tolerant of single corrupt records:
     649             :             string strType, strErr;
     650        9005 :             if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr))
     651             :             {
     652             :                 // losing keys is considered a catastrophic error, anything else
     653             :                 // we assume the user can live with:
     654           0 :                 if (IsKeyType(strType))
     655             :                     result = DB_CORRUPT;
     656             :                 else
     657             :                 {
     658             :                     // Leave other errors alone, if we try to fix them we might make things worse.
     659           0 :                     fNoncriticalErrors = true; // ... but do warn the user there is something wrong.
     660           0 :                     if (strType == "tx")
     661             :                         // Rescan if there is a bad transaction record:
     662           0 :                         SoftSetBoolArg("-rescan", true);
     663             :                 }
     664             :             }
     665        9005 :             if (!strErr.empty())
     666           0 :                 LogPrintf("%s\n", strErr);
     667             :         }
     668         119 :         pcursor->close();
     669             :     }
     670           0 :     catch (const boost::thread_interrupted&) {
     671           0 :         throw;
     672             :     }
     673           0 :     catch (...) {
     674           0 :         result = DB_CORRUPT;
     675             :     }
     676             : 
     677         119 :     if (fNoncriticalErrors && result == DB_LOAD_OK)
     678           0 :         result = DB_NONCRITICAL_ERROR;
     679             : 
     680             :     // Any wallet corruption at all: skip any rewriting or
     681             :     // upgrading, we don't want to make it worse.
     682         119 :     if (result != DB_LOAD_OK)
     683             :         return result;
     684             : 
     685         119 :     LogPrintf("nFileVersion = %d\n", wss.nFileVersion);
     686             : 
     687         238 :     LogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total\n",
     688         119 :            wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys);
     689             : 
     690             :     // nTimeFirstKey is only reliable if all keys have metadata
     691         119 :     if ((wss.nKeys + wss.nCKeys) != wss.nKeyMeta)
     692           0 :         pwallet->nTimeFirstKey = 1; // 0 would be considered 'no value'
     693             : 
     694         714 :     BOOST_FOREACH(uint256 hash, wss.vWalletUpgrade)
     695           0 :         WriteTx(hash, pwallet->mapWallet[hash]);
     696             : 
     697             :     // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
     698         119 :     if (wss.fIsEncrypted && (wss.nFileVersion == 40000 || wss.nFileVersion == 50000))
     699             :         return DB_NEED_REWRITE;
     700             : 
     701         119 :     if (wss.nFileVersion < CLIENT_VERSION) // Update
     702           0 :         WriteVersion(CLIENT_VERSION);
     703             : 
     704         119 :     if (wss.fAnyUnordered)
     705           0 :         result = ReorderTransactions(pwallet);
     706             : 
     707         119 :     return result;
     708             : }
     709             : 
     710           1 : DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& vTxHash, vector<CWalletTx>& vWtx)
     711             : {
     712           1 :     pwallet->vchDefaultKey = CPubKey();
     713           1 :     bool fNoncriticalErrors = false;
     714           1 :     DBErrors result = DB_LOAD_OK;
     715             : 
     716             :     try {
     717           1 :         LOCK(pwallet->cs_wallet);
     718           1 :         int nMinVersion = 0;
     719           3 :         if (Read((string)"minversion", nMinVersion))
     720             :         {
     721           1 :             if (nMinVersion > CLIENT_VERSION)
     722             :                 return DB_TOO_NEW;
     723           1 :             pwallet->LoadMinVersion(nMinVersion);
     724             :         }
     725             : 
     726             :         // Get cursor
     727           1 :         Dbc* pcursor = GetCursor();
     728           1 :         if (!pcursor)
     729             :         {
     730           0 :             LogPrintf("Error getting wallet database cursor\n");
     731             :             return DB_CORRUPT;
     732             :         }
     733             : 
     734             :         while (true)
     735             :         {
     736             :             // Read next record
     737             :             CDataStream ssKey(SER_DISK, CLIENT_VERSION);
     738             :             CDataStream ssValue(SER_DISK, CLIENT_VERSION);
     739          32 :             int ret = ReadAtCursor(pcursor, ssKey, ssValue);
     740          32 :             if (ret == DB_NOTFOUND)
     741             :                 break;
     742          31 :             else if (ret != 0)
     743             :             {
     744           0 :                 LogPrintf("Error reading next record from wallet database\n");
     745           0 :                 return DB_CORRUPT;
     746             :             }
     747             : 
     748             :             string strType;
     749             :             ssKey >> strType;
     750          31 :             if (strType == "tx") {
     751             :                 uint256 hash;
     752             :                 ssKey >> hash;
     753             : 
     754           6 :                 CWalletTx wtx;
     755             :                 ssValue >> wtx;
     756             : 
     757           6 :                 vTxHash.push_back(hash);
     758           6 :                 vWtx.push_back(wtx);
     759             :             }
     760             :         }
     761           1 :         pcursor->close();
     762             :     }
     763           0 :     catch (const boost::thread_interrupted&) {
     764           0 :         throw;
     765             :     }
     766           0 :     catch (...) {
     767           0 :         result = DB_CORRUPT;
     768             :     }
     769             : 
     770             :     if (fNoncriticalErrors && result == DB_LOAD_OK)
     771             :         result = DB_NONCRITICAL_ERROR;
     772             : 
     773           1 :     return result;
     774             : }
     775             : 
     776           1 : DBErrors CWalletDB::ZapWalletTx(CWallet* pwallet, vector<CWalletTx>& vWtx)
     777             : {
     778             :     // build list of wallet TXs
     779             :     vector<uint256> vTxHash;
     780           1 :     DBErrors err = FindWalletTx(pwallet, vTxHash, vWtx);
     781           1 :     if (err != DB_LOAD_OK)
     782             :         return err;
     783             : 
     784             :     // erase each wallet TX
     785          42 :     BOOST_FOREACH (uint256& hash, vTxHash) {
     786           6 :         if (!EraseTx(hash))
     787             :             return DB_CORRUPT;
     788             :     }
     789             : 
     790           1 :     return DB_LOAD_OK;
     791             : }
     792             : 
     793          94 : void ThreadFlushWalletDB(const string& strFile)
     794             : {
     795             :     // Make this thread recognisable as the wallet flushing thread
     796          94 :     RenameThread("bitcoin-wallet");
     797             : 
     798             :     static bool fOneThread;
     799          94 :     if (fOneThread)
     800             :         return;
     801          94 :     fOneThread = true;
     802         282 :     if (!GetBoolArg("-flushwallet", true))
     803             :         return;
     804             : 
     805          94 :     unsigned int nLastSeen = nWalletDBUpdated;
     806          94 :     unsigned int nLastFlushed = nWalletDBUpdated;
     807          94 :     int64_t nLastWalletUpdate = GetTime();
     808             :     while (true)
     809             :     {
     810        1934 :         MilliSleep(500);
     811             : 
     812        1840 :         if (nLastSeen != nWalletDBUpdated)
     813             :         {
     814         266 :             nLastSeen = nWalletDBUpdated;
     815         266 :             nLastWalletUpdate = GetTime();
     816             :         }
     817             : 
     818        1840 :         if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2)
     819             :         {
     820          36 :             TRY_LOCK(bitdb.cs_db,lockDb);
     821          36 :             if (lockDb)
     822             :             {
     823             :                 // Don't do this if any databases are in use
     824          36 :                 int nRefCount = 0;
     825          36 :                 map<string, int>::iterator mi = bitdb.mapFileUseCount.begin();
     826          72 :                 while (mi != bitdb.mapFileUseCount.end())
     827             :                 {
     828          36 :                     nRefCount += (*mi).second;
     829          36 :                     mi++;
     830             :                 }
     831             : 
     832          36 :                 if (nRefCount == 0)
     833             :                 {
     834          36 :                     boost::this_thread::interruption_point();
     835          36 :                     map<string, int>::iterator mi = bitdb.mapFileUseCount.find(strFile);
     836          36 :                     if (mi != bitdb.mapFileUseCount.end())
     837             :                     {
     838          36 :                         LogPrint("db", "Flushing wallet.dat\n");
     839          36 :                         nLastFlushed = nWalletDBUpdated;
     840          36 :                         int64_t nStart = GetTimeMillis();
     841             : 
     842             :                         // Flush wallet.dat so it's self contained
     843          36 :                         bitdb.CloseDb(strFile);
     844          36 :                         bitdb.CheckpointLSN(strFile);
     845             : 
     846          36 :                         bitdb.mapFileUseCount.erase(mi++);
     847          36 :                         LogPrint("db", "Flushed wallet.dat %dms\n", GetTimeMillis() - nStart);
     848             :                     }
     849             :                 }
     850             :             }
     851             :         }
     852             :     }
     853             : }
     854             : 
     855           3 : bool BackupWallet(const CWallet& wallet, const string& strDest)
     856             : {
     857           3 :     if (!wallet.fFileBacked)
     858             :         return false;
     859             :     while (true)
     860             :     {
     861             :         {
     862           3 :             LOCK(bitdb.cs_db);
     863           6 :             if (!bitdb.mapFileUseCount.count(wallet.strWalletFile) || bitdb.mapFileUseCount[wallet.strWalletFile] == 0)
     864             :             {
     865             :                 // Flush log data to the dat file
     866           3 :                 bitdb.CloseDb(wallet.strWalletFile);
     867           3 :                 bitdb.CheckpointLSN(wallet.strWalletFile);
     868           3 :                 bitdb.mapFileUseCount.erase(wallet.strWalletFile);
     869             : 
     870             :                 // Copy wallet.dat
     871           9 :                 boost::filesystem::path pathSrc = GetDataDir() / wallet.strWalletFile;
     872             :                 boost::filesystem::path pathDest(strDest);
     873           3 :                 if (boost::filesystem::is_directory(pathDest))
     874           0 :                     pathDest /= wallet.strWalletFile;
     875             : 
     876             :                 try {
     877             : #if BOOST_VERSION >= 104000
     878             :                     boost::filesystem::copy_file(pathSrc, pathDest, boost::filesystem::copy_option::overwrite_if_exists);
     879             : #else
     880             :                     boost::filesystem::copy_file(pathSrc, pathDest);
     881             : #endif
     882           3 :                     LogPrintf("copied wallet.dat to %s\n", pathDest.string());
     883             :                     return true;
     884           0 :                 } catch (const boost::filesystem::filesystem_error& e) {
     885           0 :                     LogPrintf("error copying wallet.dat to %s - %s\n", pathDest.string(), e.what());
     886             :                     return false;
     887             :                 }
     888             :             }
     889             :         }
     890           0 :         MilliSleep(100);
     891             :     }
     892           0 :     return false;
     893             : }
     894             : 
     895             : //
     896             : // Try to (very carefully!) recover wallet.dat if there is a problem.
     897             : //
     898           0 : bool CWalletDB::Recover(CDBEnv& dbenv, const std::string& filename, bool fOnlyKeys)
     899             : {
     900             :     // Recovery procedure:
     901             :     // move wallet.dat to wallet.timestamp.bak
     902             :     // Call Salvage with fAggressive=true to
     903             :     // get as much data as possible.
     904             :     // Rewrite salvaged data to wallet.dat
     905             :     // Set -rescan so any missing transactions will be
     906             :     // found.
     907           0 :     int64_t now = GetTime();
     908           0 :     std::string newFilename = strprintf("wallet.%d.bak", now);
     909             : 
     910             :     int result = dbenv.dbenv->dbrename(NULL, filename.c_str(), NULL,
     911           0 :                                        newFilename.c_str(), DB_AUTO_COMMIT);
     912           0 :     if (result == 0)
     913           0 :         LogPrintf("Renamed %s to %s\n", filename, newFilename);
     914             :     else
     915             :     {
     916           0 :         LogPrintf("Failed to rename %s to %s\n", filename, newFilename);
     917             :         return false;
     918             :     }
     919             : 
     920           0 :     std::vector<CDBEnv::KeyValPair> salvagedData;
     921           0 :     bool fSuccess = dbenv.Salvage(newFilename, true, salvagedData);
     922           0 :     if (salvagedData.empty())
     923             :     {
     924           0 :         LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename);
     925             :         return false;
     926             :     }
     927           0 :     LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size());
     928             : 
     929           0 :     boost::scoped_ptr<Db> pdbCopy(new Db(dbenv.dbenv, 0));
     930             :     int ret = pdbCopy->open(NULL,               // Txn pointer
     931             :                             filename.c_str(),   // Filename
     932             :                             "main",             // Logical db name
     933             :                             DB_BTREE,           // Database type
     934             :                             DB_CREATE,          // Flags
     935           0 :                             0);
     936           0 :     if (ret > 0)
     937             :     {
     938           0 :         LogPrintf("Cannot create database file %s\n", filename);
     939             :         return false;
     940             :     }
     941           0 :     CWallet dummyWallet;
     942             :     CWalletScanState wss;
     943             : 
     944           0 :     DbTxn* ptxn = dbenv.TxnBegin();
     945           0 :     BOOST_FOREACH(CDBEnv::KeyValPair& row, salvagedData)
     946             :     {
     947           0 :         if (fOnlyKeys)
     948             :         {
     949           0 :             CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION);
     950           0 :             CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION);
     951             :             string strType, strErr;
     952             :             bool fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue,
     953           0 :                                         wss, strType, strErr);
     954           0 :             if (!IsKeyType(strType))
     955             :                 continue;
     956           0 :             if (!fReadOK)
     957             :             {
     958           0 :                 LogPrintf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType, strErr);
     959             :                 continue;
     960             :             }
     961             :         }
     962           0 :         Dbt datKey(&row.first[0], row.first.size());
     963           0 :         Dbt datValue(&row.second[0], row.second.size());
     964           0 :         int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
     965           0 :         if (ret2 > 0)
     966           0 :             fSuccess = false;
     967           0 :     }
     968           0 :     ptxn->commit(0);
     969           0 :     pdbCopy->close(0);
     970             : 
     971           0 :     return fSuccess;
     972             : }
     973             : 
     974           0 : bool CWalletDB::Recover(CDBEnv& dbenv, const std::string& filename)
     975             : {
     976           0 :     return CWalletDB::Recover(dbenv, filename, false);
     977             : }
     978             : 
     979           0 : bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value)
     980             : {
     981           0 :     nWalletDBUpdated++;
     982           0 :     return Write(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value);
     983             : }
     984             : 
     985           0 : bool CWalletDB::EraseDestData(const std::string &address, const std::string &key)
     986             : {
     987           0 :     nWalletDBUpdated++;
     988           0 :     return Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key)));
     989         288 : }

Generated by: LCOV version 1.11