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