1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2019 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 <fs.h>
9 #include <key_io.h>
10 #include <protocol.h>
11 #include <serialize.h>
12 #include <sync.h>
13 #include <util/system.h>
14 #include <util/time.h>
15 #include <wallet/wallet.h>
16
17 #include <atomic>
18 #include <string>
19
20 #include <boost/thread.hpp>
21
22 namespace DBKeys {
23 const std::string ACENTRY{"acentry"};
24 const std::string BESTBLOCK_NOMERKLE{"bestblock_nomerkle"};
25 const std::string BESTBLOCK{"bestblock"};
26 const std::string CRYPTED_KEY{"ckey"};
27 const std::string CSCRIPT{"cscript"};
28 const std::string DEFAULTKEY{"defaultkey"};
29 const std::string DESTDATA{"destdata"};
30 const std::string FLAGS{"flags"};
31 const std::string HDCHAIN{"hdchain"};
32 const std::string KEYMETA{"keymeta"};
33 const std::string KEY{"key"};
34 const std::string MASTER_KEY{"mkey"};
35 const std::string MINVERSION{"minversion"};
36 const std::string NAME{"name"};
37 const std::string OLD_KEY{"wkey"};
38 const std::string ORDERPOSNEXT{"orderposnext"};
39 const std::string POOL{"pool"};
40 const std::string PURPOSE{"purpose"};
41 const std::string SETTINGS{"settings"};
42 const std::string TX{"tx"};
43 const std::string VERSION{"version"};
44 const std::string WATCHMETA{"watchmeta"};
45 const std::string WATCHS{"watchs"};
46 const std::string TOKEN{"token"};
47 const std::string TOKENTX{"tokentx"};
48 const std::string CONTRACTDATA{"contractdata"};
49 const std::string DELEGATION{"delegation"};
50 const std::string SUPERSTAKER{"superstaker"};
51 } // namespace DBKeys
52
53 //
54 // WalletBatch
55 //
56
WriteName(const std::string & strAddress,const std::string & strName)57 bool WalletBatch::WriteName(const std::string& strAddress, const std::string& strName)
58 {
59 return WriteIC(std::make_pair(DBKeys::NAME, strAddress), strName);
60 }
61
EraseName(const std::string & strAddress)62 bool WalletBatch::EraseName(const std::string& strAddress)
63 {
64 // This should only be used for sending addresses, never for receiving addresses,
65 // receiving addresses must always have an address book entry if they're not change return.
66 return EraseIC(std::make_pair(DBKeys::NAME, strAddress));
67 }
68
WritePurpose(const std::string & strAddress,const std::string & strPurpose)69 bool WalletBatch::WritePurpose(const std::string& strAddress, const std::string& strPurpose)
70 {
71 return WriteIC(std::make_pair(DBKeys::PURPOSE, strAddress), strPurpose);
72 }
73
ErasePurpose(const std::string & strAddress)74 bool WalletBatch::ErasePurpose(const std::string& strAddress)
75 {
76 return EraseIC(std::make_pair(DBKeys::PURPOSE, strAddress));
77 }
78
WriteTx(const CWalletTx & wtx)79 bool WalletBatch::WriteTx(const CWalletTx& wtx)
80 {
81 return WriteIC(std::make_pair(DBKeys::TX, wtx.GetHash()), wtx);
82 }
83
EraseTx(uint256 hash)84 bool WalletBatch::EraseTx(uint256 hash)
85 {
86 return EraseIC(std::make_pair(DBKeys::TX, hash));
87 }
88
WriteKeyMetadata(const CKeyMetadata & meta,const CPubKey & pubkey,const bool overwrite)89 bool WalletBatch::WriteKeyMetadata(const CKeyMetadata& meta, const CPubKey& pubkey, const bool overwrite)
90 {
91 return WriteIC(std::make_pair(DBKeys::KEYMETA, pubkey), meta, overwrite);
92 }
93
WriteKey(const CPubKey & vchPubKey,const CPrivKey & vchPrivKey,const CKeyMetadata & keyMeta)94 bool WalletBatch::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta)
95 {
96 if (!WriteKeyMetadata(keyMeta, vchPubKey, false)) {
97 return false;
98 }
99
100 // hash pubkey/privkey to accelerate wallet load
101 std::vector<unsigned char> vchKey;
102 vchKey.reserve(vchPubKey.size() + vchPrivKey.size());
103 vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
104 vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end());
105
106 return WriteIC(std::make_pair(DBKeys::KEY, vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false);
107 }
108
WriteCryptedKey(const CPubKey & vchPubKey,const std::vector<unsigned char> & vchCryptedSecret,const CKeyMetadata & keyMeta)109 bool WalletBatch::WriteCryptedKey(const CPubKey& vchPubKey,
110 const std::vector<unsigned char>& vchCryptedSecret,
111 const CKeyMetadata &keyMeta)
112 {
113 if (!WriteKeyMetadata(keyMeta, vchPubKey, true)) {
114 return false;
115 }
116
117 if (!WriteIC(std::make_pair(DBKeys::CRYPTED_KEY, vchPubKey), vchCryptedSecret, false)) {
118 return false;
119 }
120 EraseIC(std::make_pair(DBKeys::KEY, vchPubKey));
121 return true;
122 }
123
WriteMasterKey(unsigned int nID,const CMasterKey & kMasterKey)124 bool WalletBatch::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey)
125 {
126 return WriteIC(std::make_pair(DBKeys::MASTER_KEY, nID), kMasterKey, true);
127 }
128
WriteCScript(const uint160 & hash,const CScript & redeemScript)129 bool WalletBatch::WriteCScript(const uint160& hash, const CScript& redeemScript)
130 {
131 return WriteIC(std::make_pair(DBKeys::CSCRIPT, hash), redeemScript, false);
132 }
133
WriteWatchOnly(const CScript & dest,const CKeyMetadata & keyMeta)134 bool WalletBatch::WriteWatchOnly(const CScript &dest, const CKeyMetadata& keyMeta)
135 {
136 if (!WriteIC(std::make_pair(DBKeys::WATCHMETA, dest), keyMeta)) {
137 return false;
138 }
139 return WriteIC(std::make_pair(DBKeys::WATCHS, dest), '1');
140 }
141
EraseWatchOnly(const CScript & dest)142 bool WalletBatch::EraseWatchOnly(const CScript &dest)
143 {
144 if (!EraseIC(std::make_pair(DBKeys::WATCHMETA, dest))) {
145 return false;
146 }
147 return EraseIC(std::make_pair(DBKeys::WATCHS, dest));
148 }
149
WriteBestBlock(const CBlockLocator & locator)150 bool WalletBatch::WriteBestBlock(const CBlockLocator& locator)
151 {
152 WriteIC(DBKeys::BESTBLOCK, CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan
153 return WriteIC(DBKeys::BESTBLOCK_NOMERKLE, locator);
154 }
155
ReadBestBlock(CBlockLocator & locator)156 bool WalletBatch::ReadBestBlock(CBlockLocator& locator)
157 {
158 if (m_batch.Read(DBKeys::BESTBLOCK, locator) && !locator.vHave.empty()) return true;
159 return m_batch.Read(DBKeys::BESTBLOCK_NOMERKLE, locator);
160 }
161
WriteOrderPosNext(int64_t nOrderPosNext)162 bool WalletBatch::WriteOrderPosNext(int64_t nOrderPosNext)
163 {
164 return WriteIC(DBKeys::ORDERPOSNEXT, nOrderPosNext);
165 }
166
ReadPool(int64_t nPool,CKeyPool & keypool)167 bool WalletBatch::ReadPool(int64_t nPool, CKeyPool& keypool)
168 {
169 return m_batch.Read(std::make_pair(DBKeys::POOL, nPool), keypool);
170 }
171
WritePool(int64_t nPool,const CKeyPool & keypool)172 bool WalletBatch::WritePool(int64_t nPool, const CKeyPool& keypool)
173 {
174 return WriteIC(std::make_pair(DBKeys::POOL, nPool), keypool);
175 }
176
ErasePool(int64_t nPool)177 bool WalletBatch::ErasePool(int64_t nPool)
178 {
179 return EraseIC(std::make_pair(DBKeys::POOL, nPool));
180 }
181
WriteMinVersion(int nVersion)182 bool WalletBatch::WriteMinVersion(int nVersion)
183 {
184 return WriteIC(DBKeys::MINVERSION, nVersion);
185 }
186
187 class CWalletScanState {
188 public:
189 unsigned int nKeys{0};
190 unsigned int nCKeys{0};
191 unsigned int nWatchKeys{0};
192 unsigned int nKeyMeta{0};
193 unsigned int m_unknown_records{0};
194 bool fIsEncrypted{false};
195 bool fAnyUnordered{false};
196 std::vector<uint256> vWalletUpgrade;
197
CWalletScanState()198 CWalletScanState() {
199 }
200 };
201
202 static bool
ReadKeyValue(CWallet * pwallet,CDataStream & ssKey,CDataStream & ssValue,CWalletScanState & wss,std::string & strType,std::string & strErr)203 ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
204 CWalletScanState &wss, std::string& strType, std::string& strErr) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
205 {
206 try {
207 // Unserialize
208 // Taking advantage of the fact that pair serialization
209 // is just the two items serialized one after the other
210 ssKey >> strType;
211 if (strType == DBKeys::NAME) {
212 std::string strAddress;
213 ssKey >> strAddress;
214 std::string label;
215 ssValue >> label;
216 pwallet->m_address_book[DecodeDestination(strAddress)].SetLabel(label);
217 } else if (strType == DBKeys::PURPOSE) {
218 std::string strAddress;
219 ssKey >> strAddress;
220 ssValue >> pwallet->m_address_book[DecodeDestination(strAddress)].purpose;
221 } else if (strType == DBKeys::TX) {
222 uint256 hash;
223 ssKey >> hash;
224 CWalletTx wtx(nullptr /* pwallet */, MakeTransactionRef());
225 ssValue >> wtx;
226 if (wtx.GetHash() != hash)
227 return false;
228
229 // Undo serialize changes in 31600
230 if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
231 {
232 if (!ssValue.empty())
233 {
234 char fTmp;
235 char fUnused;
236 std::string unused_string;
237 ssValue >> fTmp >> fUnused >> unused_string;
238 strErr = strprintf("LoadWallet() upgrading tx ver=%d %d %s",
239 wtx.fTimeReceivedIsTxTime, fTmp, hash.ToString());
240 wtx.fTimeReceivedIsTxTime = fTmp;
241 }
242 else
243 {
244 strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString());
245 wtx.fTimeReceivedIsTxTime = 0;
246 }
247 wss.vWalletUpgrade.push_back(hash);
248 }
249
250 if (wtx.nOrderPos == -1)
251 wss.fAnyUnordered = true;
252
253 pwallet->LoadToWallet(wtx);
254 } else if (strType == DBKeys::WATCHS) {
255 wss.nWatchKeys++;
256 CScript script;
257 ssKey >> script;
258 char fYes;
259 ssValue >> fYes;
260 if (fYes == '1') {
261 pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadWatchOnly(script);
262 }
263 } else if (strType == DBKeys::KEY) {
264 CPubKey vchPubKey;
265 ssKey >> vchPubKey;
266 if (!vchPubKey.IsValid())
267 {
268 strErr = "Error reading wallet database: CPubKey corrupt";
269 return false;
270 }
271 CKey key;
272 CPrivKey pkey;
273 uint256 hash;
274
275 wss.nKeys++;
276 ssValue >> pkey;
277
278 // Old wallets store keys as DBKeys::KEY [pubkey] => [privkey]
279 // ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key
280 // using EC operations as a checksum.
281 // Newer wallets store keys as DBKeys::KEY [pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while
282 // remaining backwards-compatible.
283 try
284 {
285 ssValue >> hash;
286 }
287 catch (...) {}
288
289 bool fSkipCheck = false;
290
291 if (!hash.IsNull())
292 {
293 // hash pubkey/privkey to accelerate wallet load
294 std::vector<unsigned char> vchKey;
295 vchKey.reserve(vchPubKey.size() + pkey.size());
296 vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
297 vchKey.insert(vchKey.end(), pkey.begin(), pkey.end());
298
299 if (Hash(vchKey.begin(), vchKey.end()) != hash)
300 {
301 strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt";
302 return false;
303 }
304
305 fSkipCheck = true;
306 }
307
308 if (!key.Load(pkey, vchPubKey, fSkipCheck))
309 {
310 strErr = "Error reading wallet database: CPrivKey corrupt";
311 return false;
312 }
313 if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKey(key, vchPubKey))
314 {
315 strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadKey failed";
316 return false;
317 }
318 } else if (strType == DBKeys::MASTER_KEY) {
319 // Master encryption key is loaded into only the wallet and not any of the ScriptPubKeyMans.
320 unsigned int nID;
321 ssKey >> nID;
322 CMasterKey kMasterKey;
323 ssValue >> kMasterKey;
324 if(pwallet->mapMasterKeys.count(nID) != 0)
325 {
326 strErr = strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID);
327 return false;
328 }
329 pwallet->mapMasterKeys[nID] = kMasterKey;
330 if (pwallet->nMasterKeyMaxID < nID)
331 pwallet->nMasterKeyMaxID = nID;
332 } else if (strType == DBKeys::CRYPTED_KEY) {
333 CPubKey vchPubKey;
334 ssKey >> vchPubKey;
335 if (!vchPubKey.IsValid())
336 {
337 strErr = "Error reading wallet database: CPubKey corrupt";
338 return false;
339 }
340 std::vector<unsigned char> vchPrivKey;
341 ssValue >> vchPrivKey;
342 wss.nCKeys++;
343
344 if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadCryptedKey(vchPubKey, vchPrivKey))
345 {
346 strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadCryptedKey failed";
347 return false;
348 }
349 wss.fIsEncrypted = true;
350 } else if (strType == DBKeys::KEYMETA) {
351 CPubKey vchPubKey;
352 ssKey >> vchPubKey;
353 CKeyMetadata keyMeta;
354 ssValue >> keyMeta;
355 wss.nKeyMeta++;
356 pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKeyMetadata(vchPubKey.GetID(), keyMeta);
357 } else if (strType == DBKeys::WATCHMETA) {
358 CScript script;
359 ssKey >> script;
360 CKeyMetadata keyMeta;
361 ssValue >> keyMeta;
362 wss.nKeyMeta++;
363 pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadScriptMetadata(CScriptID(script), keyMeta);
364 } else if (strType == DBKeys::DEFAULTKEY) {
365 // We don't want or need the default key, but if there is one set,
366 // we want to make sure that it is valid so that we can detect corruption
367 CPubKey vchPubKey;
368 ssValue >> vchPubKey;
369 if (!vchPubKey.IsValid()) {
370 strErr = "Error reading wallet database: Default Key corrupt";
371 return false;
372 }
373 } else if (strType == DBKeys::POOL) {
374 int64_t nIndex;
375 ssKey >> nIndex;
376 CKeyPool keypool;
377 ssValue >> keypool;
378
379 pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKeyPool(nIndex, keypool);
380 } else if (strType == DBKeys::CSCRIPT) {
381 uint160 hash;
382 ssKey >> hash;
383 CScript script;
384 ssValue >> script;
385 if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadCScript(script))
386 {
387 strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadCScript failed";
388 return false;
389 }
390 } else if (strType == DBKeys::ORDERPOSNEXT) {
391 ssValue >> pwallet->nOrderPosNext;
392 } else if (strType == DBKeys::DESTDATA) {
393 std::string strAddress, strKey, strValue;
394 ssKey >> strAddress;
395 ssKey >> strKey;
396 ssValue >> strValue;
397 pwallet->LoadDestData(DecodeDestination(strAddress), strKey, strValue);
398 } else if (strType == DBKeys::HDCHAIN) {
399 CHDChain chain;
400 ssValue >> chain;
401 pwallet->GetOrCreateLegacyScriptPubKeyMan()->SetHDChain(chain, true);
402 }
403 else if (strType == DBKeys::TOKEN)
404 {
405 uint256 hash;
406 ssKey >> hash;
407 CTokenInfo wtoken;
408 ssValue >> wtoken;
409 if (wtoken.GetHash() != hash)
410 {
411 strErr = "Error reading wallet database: CTokenInfo corrupt";
412 return false;
413 }
414
415 pwallet->LoadToken(wtoken);
416 }
417 else if (strType == DBKeys::TOKENTX)
418 {
419 uint256 hash;
420 ssKey >> hash;
421 CTokenTx wTokenTx;
422 ssValue >> wTokenTx;
423 if (wTokenTx.GetHash() != hash)
424 {
425 strErr = "Error reading wallet database: CTokenTx corrupt";
426 return false;
427 }
428
429 pwallet->LoadTokenTx(wTokenTx);
430 }
431 else if (strType == DBKeys::DELEGATION)
432 {
433 uint256 hash;
434 ssKey >> hash;
435 CDelegationInfo wdelegation;
436 ssValue >> wdelegation;
437 if (wdelegation.GetHash() != hash)
438 {
439 strErr = "Error reading wallet database: CDelegationInfo corrupt";
440 return false;
441 }
442
443 pwallet->LoadDelegation(wdelegation);
444 }
445 else if (strType == DBKeys::SUPERSTAKER)
446 {
447 uint256 hash;
448 ssKey >> hash;
449 CSuperStakerInfo wsuperStaker;
450 ssValue >> wsuperStaker;
451 if (wsuperStaker.GetHash() != hash)
452 {
453 strErr = "Error reading wallet database: CSuperStakerInfo corrupt";
454 return false;
455 }
456
457 pwallet->LoadSuperStaker(wsuperStaker);
458 }
459 else if (strType == DBKeys::CONTRACTDATA)
460 {
461 std::string strAddress, strKey, strValue;
462 ssKey >> strAddress;
463 ssKey >> strKey;
464 ssValue >> strValue;
465 if (!pwallet->LoadContractData(strAddress, strKey, strValue))
466 {
467 strErr = "Error reading wallet database: LoadContractData failed";
468 return false;
469 }
470 } else if (strType == DBKeys::FLAGS) {
471 uint64_t flags;
472 ssValue >> flags;
473 if (!pwallet->SetWalletFlags(flags, true)) {
474 strErr = "Error reading wallet database: Unknown non-tolerable wallet flags found";
475 return false;
476 }
477 } else if (strType == DBKeys::OLD_KEY) {
478 strErr = "Found unsupported 'wkey' record, try loading with version 0.18";
479 return false;
480 } else if (strType != DBKeys::BESTBLOCK && strType != DBKeys::BESTBLOCK_NOMERKLE &&
481 strType != DBKeys::MINVERSION && strType != DBKeys::ACENTRY &&
482 strType != DBKeys::VERSION && strType != DBKeys::SETTINGS) {
483 wss.m_unknown_records++;
484 }
485 } catch (const std::exception& e) {
486 if (strErr.empty()) {
487 strErr = e.what();
488 }
489 return false;
490 } catch (...) {
491 if (strErr.empty()) {
492 strErr = "Caught unknown exception in ReadKeyValue";
493 }
494 return false;
495 }
496 return true;
497 }
498
IsKeyType(const std::string & strType)499 bool WalletBatch::IsKeyType(const std::string& strType)
500 {
501 return (strType == DBKeys::KEY ||
502 strType == DBKeys::MASTER_KEY || strType == DBKeys::CRYPTED_KEY);
503 }
504
LoadWallet(CWallet * pwallet)505 DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
506 {
507 CWalletScanState wss;
508 bool fNoncriticalErrors = false;
509 DBErrors result = DBErrors::LOAD_OK;
510
511 LOCK(pwallet->cs_wallet);
512 try {
513 int nMinVersion = 0;
514 if (m_batch.Read(DBKeys::MINVERSION, nMinVersion)) {
515 if (nMinVersion > FEATURE_LATEST)
516 return DBErrors::TOO_NEW;
517 pwallet->LoadMinVersion(nMinVersion);
518 }
519
520 // Get cursor
521 Dbc* pcursor = m_batch.GetCursor();
522 if (!pcursor)
523 {
524 pwallet->WalletLogPrintf("Error getting wallet database cursor\n");
525 return DBErrors::CORRUPT;
526 }
527
528 while (true)
529 {
530 // Read next record
531 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
532 CDataStream ssValue(SER_DISK, CLIENT_VERSION);
533 int ret = m_batch.ReadAtCursor(pcursor, ssKey, ssValue);
534 if (ret == DB_NOTFOUND)
535 break;
536 else if (ret != 0)
537 {
538 pwallet->WalletLogPrintf("Error reading next record from wallet database\n");
539 return DBErrors::CORRUPT;
540 }
541
542 // Try to be tolerant of single corrupt records:
543 std::string strType, strErr;
544 if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr))
545 {
546 // losing keys is considered a catastrophic error, anything else
547 // we assume the user can live with:
548 if (IsKeyType(strType) || strType == DBKeys::DEFAULTKEY) {
549 result = DBErrors::CORRUPT;
550 } else if (strType == DBKeys::FLAGS) {
551 // reading the wallet flags can only fail if unknown flags are present
552 result = DBErrors::TOO_NEW;
553 } else {
554 // Leave other errors alone, if we try to fix them we might make things worse.
555 fNoncriticalErrors = true; // ... but do warn the user there is something wrong.
556 if (strType == DBKeys::TX)
557 // Rescan if there is a bad transaction record:
558 gArgs.SoftSetBoolArg("-rescan", true);
559 }
560 }
561 if (!strErr.empty())
562 pwallet->WalletLogPrintf("%s\n", strErr);
563 }
564 pcursor->close();
565 }
566 catch (const boost::thread_interrupted&) {
567 throw;
568 }
569 catch (...) {
570 result = DBErrors::CORRUPT;
571 }
572
573 if (fNoncriticalErrors && result == DBErrors::LOAD_OK)
574 result = DBErrors::NONCRITICAL_ERROR;
575
576 // Any wallet corruption at all: skip any rewriting or
577 // upgrading, we don't want to make it worse.
578 if (result != DBErrors::LOAD_OK)
579 return result;
580
581 // Last client version to open this wallet, was previously the file version number
582 int last_client = CLIENT_VERSION;
583 m_batch.Read(DBKeys::VERSION, last_client);
584
585 int wallet_version = pwallet->GetVersion();
586 pwallet->WalletLogPrintf("Wallet File Version = %d\n", wallet_version > 0 ? wallet_version : last_client);
587
588 pwallet->WalletLogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total. Unknown wallet records: %u\n",
589 wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys, wss.m_unknown_records);
590
591 // nTimeFirstKey is only reliable if all keys have metadata
592 if ((wss.nKeys + wss.nCKeys + wss.nWatchKeys) != wss.nKeyMeta) {
593 auto spk_man = pwallet->GetOrCreateLegacyScriptPubKeyMan();
594 if (spk_man) {
595 LOCK(spk_man->cs_KeyStore);
596 spk_man->UpdateTimeFirstKey(1);
597 }
598 }
599
600 for (const uint256& hash : wss.vWalletUpgrade)
601 WriteTx(pwallet->mapWallet.at(hash));
602
603 // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
604 if (wss.fIsEncrypted && (last_client == 40000 || last_client == 50000))
605 return DBErrors::NEED_REWRITE;
606
607 if (last_client < CLIENT_VERSION) // Update
608 m_batch.Write(DBKeys::VERSION, CLIENT_VERSION);
609
610 if (wss.fAnyUnordered)
611 result = pwallet->ReorderTransactions();
612
613 // Upgrade all of the wallet keymetadata to have the hd master key id
614 // This operation is not atomic, but if it fails, updated entries are still backwards compatible with older software
615 try {
616 pwallet->UpgradeKeyMetadata();
617 } catch (...) {
618 result = DBErrors::CORRUPT;
619 }
620
621 return result;
622 }
623
FindWalletTx(std::vector<uint256> & vTxHash,std::vector<CWalletTx> & vWtx)624 DBErrors WalletBatch::FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx)
625 {
626 DBErrors result = DBErrors::LOAD_OK;
627
628 try {
629 int nMinVersion = 0;
630 if (m_batch.Read(DBKeys::MINVERSION, nMinVersion)) {
631 if (nMinVersion > FEATURE_LATEST)
632 return DBErrors::TOO_NEW;
633 }
634
635 // Get cursor
636 Dbc* pcursor = m_batch.GetCursor();
637 if (!pcursor)
638 {
639 LogPrintf("Error getting wallet database cursor\n");
640 return DBErrors::CORRUPT;
641 }
642
643 while (true)
644 {
645 // Read next record
646 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
647 CDataStream ssValue(SER_DISK, CLIENT_VERSION);
648 int ret = m_batch.ReadAtCursor(pcursor, ssKey, ssValue);
649 if (ret == DB_NOTFOUND)
650 break;
651 else if (ret != 0)
652 {
653 LogPrintf("Error reading next record from wallet database\n");
654 return DBErrors::CORRUPT;
655 }
656
657 std::string strType;
658 ssKey >> strType;
659 if (strType == DBKeys::TX) {
660 uint256 hash;
661 ssKey >> hash;
662
663 CWalletTx wtx(nullptr /* pwallet */, MakeTransactionRef());
664 ssValue >> wtx;
665
666 vTxHash.push_back(hash);
667 vWtx.push_back(wtx);
668 }
669 }
670 pcursor->close();
671 }
672 catch (const boost::thread_interrupted&) {
673 throw;
674 }
675 catch (...) {
676 result = DBErrors::CORRUPT;
677 }
678
679 return result;
680 }
681
ZapSelectTx(std::vector<uint256> & vTxHashIn,std::vector<uint256> & vTxHashOut)682 DBErrors WalletBatch::ZapSelectTx(std::vector<uint256>& vTxHashIn, std::vector<uint256>& vTxHashOut)
683 {
684 // build list of wallet TXs and hashes
685 std::vector<uint256> vTxHash;
686 std::vector<CWalletTx> vWtx;
687 DBErrors err = FindWalletTx(vTxHash, vWtx);
688 if (err != DBErrors::LOAD_OK) {
689 return err;
690 }
691
692 std::sort(vTxHash.begin(), vTxHash.end());
693 std::sort(vTxHashIn.begin(), vTxHashIn.end());
694
695 // erase each matching wallet TX
696 bool delerror = false;
697 std::vector<uint256>::iterator it = vTxHashIn.begin();
698 for (const uint256& hash : vTxHash) {
699 while (it < vTxHashIn.end() && (*it) < hash) {
700 it++;
701 }
702 if (it == vTxHashIn.end()) {
703 break;
704 }
705 else if ((*it) == hash) {
706 if(!EraseTx(hash)) {
707 LogPrint(BCLog::WALLETDB, "Transaction was found for deletion but returned database error: %s\n", hash.GetHex());
708 delerror = true;
709 }
710 vTxHashOut.push_back(hash);
711 }
712 }
713
714 if (delerror) {
715 return DBErrors::CORRUPT;
716 }
717 return DBErrors::LOAD_OK;
718 }
719
ZapWalletTx(std::vector<CWalletTx> & vWtx)720 DBErrors WalletBatch::ZapWalletTx(std::vector<CWalletTx>& vWtx)
721 {
722 // build list of wallet TXs
723 std::vector<uint256> vTxHash;
724 DBErrors err = FindWalletTx(vTxHash, vWtx);
725 if (err != DBErrors::LOAD_OK)
726 return err;
727
728 // erase each wallet TX
729 for (const uint256& hash : vTxHash) {
730 if (!EraseTx(hash))
731 return DBErrors::CORRUPT;
732 }
733
734 return DBErrors::LOAD_OK;
735 }
736
MaybeCompactWalletDB()737 void MaybeCompactWalletDB()
738 {
739 static std::atomic<bool> fOneThread(false);
740 if (fOneThread.exchange(true)) {
741 return;
742 }
743 if (!gArgs.GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) {
744 return;
745 }
746
747 for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
748 WalletDatabase& dbh = pwallet->GetDBHandle();
749
750 unsigned int nUpdateCounter = dbh.nUpdateCounter;
751
752 if (dbh.nLastSeen != nUpdateCounter) {
753 dbh.nLastSeen = nUpdateCounter;
754 dbh.nLastWalletUpdate = GetTime();
755 }
756
757 if (dbh.nLastFlushed != nUpdateCounter && GetTime() - dbh.nLastWalletUpdate >= 2) {
758 if (BerkeleyBatch::PeriodicFlush(dbh)) {
759 dbh.nLastFlushed = nUpdateCounter;
760 }
761 }
762 }
763
764 fOneThread = false;
765 }
766
767 //
768 // Try to (very carefully!) recover wallet file if there is a problem.
769 //
Recover(const fs::path & wallet_path,void * callbackDataIn,bool (* recoverKVcallback)(void * callbackData,CDataStream ssKey,CDataStream ssValue),std::string & out_backup_filename)770 bool WalletBatch::Recover(const fs::path& wallet_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename)
771 {
772 return BerkeleyBatch::Recover(wallet_path, callbackDataIn, recoverKVcallback, out_backup_filename);
773 }
774
Recover(const fs::path & wallet_path,std::string & out_backup_filename)775 bool WalletBatch::Recover(const fs::path& wallet_path, std::string& out_backup_filename)
776 {
777 // recover without a key filter callback
778 // results in recovering all record types
779 return WalletBatch::Recover(wallet_path, nullptr, nullptr, out_backup_filename);
780 }
781
RecoverKeysOnlyFilter(void * callbackData,CDataStream ssKey,CDataStream ssValue)782 bool WalletBatch::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue)
783 {
784 CWallet *dummyWallet = reinterpret_cast<CWallet*>(callbackData);
785 CWalletScanState dummyWss;
786 std::string strType, strErr;
787 bool fReadOK;
788 {
789 // Required in LoadKeyMetadata():
790 LOCK(dummyWallet->cs_wallet);
791 fReadOK = ReadKeyValue(dummyWallet, ssKey, ssValue,
792 dummyWss, strType, strErr);
793 }
794 if (!IsKeyType(strType) && strType != DBKeys::HDCHAIN && strType != DBKeys::TOKEN && strType != DBKeys::TOKENTX && strType != DBKeys::DELEGATION) {
795 return false;
796 }
797 if (!fReadOK)
798 {
799 LogPrintf("WARNING: WalletBatch::Recover skipping %s: %s\n", strType, strErr);
800 return false;
801 }
802
803 return true;
804 }
805
VerifyEnvironment(const fs::path & wallet_path,std::string & errorStr)806 bool WalletBatch::VerifyEnvironment(const fs::path& wallet_path, std::string& errorStr)
807 {
808 return BerkeleyBatch::VerifyEnvironment(wallet_path, errorStr);
809 }
810
VerifyDatabaseFile(const fs::path & wallet_path,std::vector<std::string> & warnings,std::string & errorStr)811 bool WalletBatch::VerifyDatabaseFile(const fs::path& wallet_path, std::vector<std::string>& warnings, std::string& errorStr)
812 {
813 return BerkeleyBatch::VerifyDatabaseFile(wallet_path, warnings, errorStr, WalletBatch::Recover);
814 }
815
WriteDestData(const std::string & address,const std::string & key,const std::string & value)816 bool WalletBatch::WriteDestData(const std::string &address, const std::string &key, const std::string &value)
817 {
818 return WriteIC(std::make_pair(DBKeys::DESTDATA, std::make_pair(address, key)), value);
819 }
820
EraseDestData(const std::string & address,const std::string & key)821 bool WalletBatch::EraseDestData(const std::string &address, const std::string &key)
822 {
823 return EraseIC(std::make_pair(DBKeys::DESTDATA, std::make_pair(address, key)));
824 }
825
826
WriteHDChain(const CHDChain & chain)827 bool WalletBatch::WriteHDChain(const CHDChain& chain)
828 {
829 return WriteIC(DBKeys::HDCHAIN, chain);
830 }
831
WriteWalletFlags(const uint64_t flags)832 bool WalletBatch::WriteWalletFlags(const uint64_t flags)
833 {
834 return WriteIC(DBKeys::FLAGS, flags);
835 }
836
TxnBegin()837 bool WalletBatch::TxnBegin()
838 {
839 return m_batch.TxnBegin();
840 }
841
TxnCommit()842 bool WalletBatch::TxnCommit()
843 {
844 return m_batch.TxnCommit();
845 }
846
TxnAbort()847 bool WalletBatch::TxnAbort()
848 {
849 return m_batch.TxnAbort();
850 }
851
WriteToken(const CTokenInfo & wtoken)852 bool WalletBatch::WriteToken(const CTokenInfo &wtoken)
853 {
854 return WriteIC(std::make_pair(DBKeys::TOKEN, wtoken.GetHash()), wtoken);
855 }
856
EraseToken(uint256 hash)857 bool WalletBatch::EraseToken(uint256 hash)
858 {
859 return EraseIC(std::make_pair(DBKeys::TOKEN, hash));
860 }
861
WriteTokenTx(const CTokenTx & wTokenTx)862 bool WalletBatch::WriteTokenTx(const CTokenTx &wTokenTx)
863 {
864 return WriteIC(std::make_pair(DBKeys::TOKENTX, wTokenTx.GetHash()), wTokenTx);
865 }
866
EraseTokenTx(uint256 hash)867 bool WalletBatch::EraseTokenTx(uint256 hash)
868 {
869 return EraseIC(std::make_pair(DBKeys::TOKENTX, hash));
870 }
871
WriteContractData(const std::string & address,const std::string & key,const std::string & value)872 bool WalletBatch::WriteContractData(const std::string &address, const std::string &key, const std::string &value)
873 {
874 return WriteIC(std::make_pair(DBKeys::CONTRACTDATA, std::make_pair(address, key)), value);
875 }
876
EraseContractData(const std::string & address,const std::string & key)877 bool WalletBatch::EraseContractData(const std::string &address, const std::string &key)
878 {
879 return EraseIC(std::make_pair(DBKeys::CONTRACTDATA, std::make_pair(address, key)));
880 }
881
WriteDelegation(const CDelegationInfo & wdelegation)882 bool WalletBatch::WriteDelegation(const CDelegationInfo &wdelegation)
883 {
884 return WriteIC(std::make_pair(DBKeys::DELEGATION, wdelegation.GetHash()), wdelegation);
885 }
886
EraseDelegation(uint256 hash)887 bool WalletBatch::EraseDelegation(uint256 hash)
888 {
889 return EraseIC(std::make_pair(DBKeys::DELEGATION, hash));
890 }
891
WriteSuperStaker(const CSuperStakerInfo & wsuperStaker)892 bool WalletBatch::WriteSuperStaker(const CSuperStakerInfo &wsuperStaker)
893 {
894 return WriteIC(std::make_pair(DBKeys::SUPERSTAKER, wsuperStaker.GetHash()), wsuperStaker);
895 }
896
EraseSuperStaker(uint256 hash)897 bool WalletBatch::EraseSuperStaker(uint256 hash)
898 {
899 return EraseIC(std::make_pair(DBKeys::SUPERSTAKER, hash));
900 }
901