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