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