]>
Commit | Line | Data |
---|---|---|
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 | 24 | using namespace std; |
9eace6b1 | 25 | |
51ed9ec9 | 26 | static uint64_t nAccountingEntryNumber = 0; |
9eace6b1 | 27 | |
9eace6b1 JG |
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 | ||
a41d5fe0 GA |
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 | ||
51ed9ec9 BD |
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 | ||
fabba0e6 PK |
87 | bool 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 | 108 | bool 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 |
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 | ||
c1c45943 S |
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 | ||
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 |
145 | bool CWalletDB::WriteViewingKey(const libzcash::ViewingKey &vk) |
146 | { | |
147 | nWalletDBUpdated++; | |
148 | return Write(std::make_pair(std::string("vkey"), vk), '1'); | |
149 | } | |
150 | ||
151 | bool CWalletDB::EraseViewingKey(const libzcash::ViewingKey &vk) | |
152 | { | |
153 | nWalletDBUpdated++; | |
154 | return Erase(std::make_pair(std::string("vkey"), vk)); | |
155 | } | |
156 | ||
51ed9ec9 BD |
157 | bool 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 | 163 | bool 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 |
169 | bool 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 |
175 | bool CWalletDB::WriteBestBlock(const CBlockLocator& locator) |
176 | { | |
177 | nWalletDBUpdated++; | |
178 | return Write(std::string("bestblock"), locator); | |
179 | } | |
180 | ||
181 | bool CWalletDB::ReadBestBlock(CBlockLocator& locator) | |
182 | { | |
183 | return Read(std::string("bestblock"), locator); | |
184 | } | |
185 | ||
186 | bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext) | |
187 | { | |
188 | nWalletDBUpdated++; | |
189 | return Write(std::string("orderposnext"), nOrderPosNext); | |
190 | } | |
191 | ||
192 | bool CWalletDB::WriteDefaultKey(const CPubKey& vchPubKey) | |
193 | { | |
194 | nWalletDBUpdated++; | |
195 | return Write(std::string("defaultkey"), vchPubKey); | |
196 | } | |
197 | ||
4086e5ce | 198 | bool CWalletDB::WriteWitnessCacheSize(int64_t nWitnessCacheSize) |
be74c80d JG |
199 | { |
200 | nWalletDBUpdated++; | |
4086e5ce | 201 | return Write(std::string("witnesscachesize"), nWitnessCacheSize); |
be74c80d JG |
202 | } |
203 | ||
51ed9ec9 BD |
204 | bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool) |
205 | { | |
206 | return Read(std::make_pair(std::string("pool"), nPool), keypool); | |
207 | } | |
208 | ||
209 | bool CWalletDB::WritePool(int64_t nPool, const CKeyPool& keypool) | |
210 | { | |
211 | nWalletDBUpdated++; | |
212 | return Write(std::make_pair(std::string("pool"), nPool), keypool); | |
213 | } | |
214 | ||
215 | bool CWalletDB::ErasePool(int64_t nPool) | |
216 | { | |
217 | nWalletDBUpdated++; | |
218 | return Erase(std::make_pair(std::string("pool"), nPool)); | |
219 | } | |
220 | ||
51ed9ec9 BD |
221 | bool CWalletDB::WriteMinVersion(int nVersion) |
222 | { | |
223 | return Write(std::string("minversion"), nVersion); | |
224 | } | |
225 | ||
9eace6b1 JG |
226 | bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account) |
227 | { | |
228 | account.SetNull(); | |
229 | return Read(make_pair(string("acc"), strAccount), account); | |
230 | } | |
231 | ||
232 | bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account) | |
233 | { | |
234 | return Write(make_pair(string("acc"), strAccount), account); | |
235 | } | |
236 | ||
51ed9ec9 | 237 | bool 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 |
242 | bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry) |
243 | { | |
9c7722b7 | 244 | return WriteAccountingEntry(++nAccountingEntryNumber, acentry); |
9eace6b1 JG |
245 | } |
246 | ||
a372168e | 247 | CAmount 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 | ||
259 | void 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 | 302 | DBErrors 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 |
378 | class CWalletScanState { |
379 | public: | |
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 |
399 | bool |
400 | ReadKeyValue(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 | ||
718 | static 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 | ||
726 | DBErrors 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 | 830 | DBErrors 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 | 896 | DBErrors 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 | 913 | void 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 | ||
975 | bool 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 | 1014 | bool 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 | 1090 | bool CWalletDB::Recover(CDBEnv& dbenv, const std::string& filename) |
eed1785f GA |
1091 | { |
1092 | return CWalletDB::Recover(dbenv, filename, false); | |
1093 | } | |
b10e1470 WL |
1094 | |
1095 | bool 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 | ||
1101 | bool 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 | } |