1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2012 The Bitcoin developers
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
13 #include <boost/version.hpp>
14 #include <boost/filesystem.hpp>
18 static uint64_t nAccountingEntryNumber = 0;
19 extern bool fWalletUnlockMintOnly;
25 CKeyMetadata::CKeyMetadata()
30 CKeyMetadata::CKeyMetadata(int64_t nCreateTime_)
32 nVersion = CKeyMetadata::CURRENT_VERSION;
33 nCreateTime = nCreateTime_;
36 void CKeyMetadata::SetNull()
38 nVersion = CKeyMetadata::CURRENT_VERSION;
46 CWalletDB::CWalletDB(string strFilename, const char* pszMode) : CDB(strFilename.c_str(), pszMode) {}
48 bool CWalletDB::WriteName(const string& strAddress, const string& strName)
51 return Write(make_pair(string("name"), strAddress), strName);
54 bool CWalletDB::EraseName(const string& strAddress)
56 // This should only be used for sending addresses, never for receiving addresses,
57 // receiving addresses must always have an address book entry if they're not change return.
59 return Erase(make_pair(string("name"), strAddress));
62 bool CWalletDB::WriteTx(uint256 hash, const CWalletTx& wtx)
65 return Write(make_pair(string("tx"), hash), wtx);
68 bool CWalletDB::EraseTx(uint256 hash)
71 return Erase(make_pair(string("tx"), hash));
74 bool CWalletDB::WriteKey(const CPubKey& key, const CPrivKey& vchPrivKey, const CKeyMetadata &keyMeta)
77 if(!Write(make_pair(string("keymeta"), key), keyMeta))
80 if(!Write(make_pair(string("key"), key), vchPrivKey, false))
86 bool CWalletDB::WriteMalleableKey(const CMalleableKeyView& keyView, const CSecret& vchSecretH, const CKeyMetadata &keyMeta)
89 if(!Write(make_pair(string("malmeta"), keyView.ToString()), keyMeta))
92 if(!Write(make_pair(string("malpair"), keyView.ToString()), vchSecretH, false))
98 bool CWalletDB::WriteCryptedMalleableKey(const CMalleableKeyView& keyView, const vector<unsigned char>& vchCryptedSecretH, const CKeyMetadata &keyMeta)
101 if(!Write(make_pair(string("malmeta"), keyView.ToString()), keyMeta))
104 if(!Write(make_pair(string("malcpair"), keyView.ToString()), vchCryptedSecretH, false))
107 Erase(make_pair(string("malpair"), keyView.ToString()));
112 bool CWalletDB::WriteCryptedKey(const CPubKey& key, const vector<unsigned char>& vchCryptedSecret, const CKeyMetadata &keyMeta)
115 bool fEraseUnencryptedKey = true;
117 if(!Write(make_pair(string("keymeta"), key), keyMeta))
120 if (!Write(make_pair(string("ckey"), key), vchCryptedSecret, false))
122 if (fEraseUnencryptedKey)
124 Erase(make_pair(string("key"), key));
125 Erase(make_pair(string("wkey"), key));
130 bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey)
133 return Write(make_pair(string("mkey"), nID), kMasterKey, true);
136 bool CWalletDB::EraseMasterKey(unsigned int nID)
139 return Erase(make_pair(string("mkey"), nID));
142 bool CWalletDB::EraseCryptedKey(const CPubKey& key)
144 return Erase(make_pair(string("ckey"), key));
147 bool CWalletDB::EraseCryptedMalleableKey(const CMalleableKeyView& keyView)
149 return Erase(make_pair(string("malcpair"), keyView.ToString()));
152 bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript)
155 return Write(make_pair(string("cscript"), hash), redeemScript, false);
158 bool CWalletDB::WriteWatchOnly(const CScript &dest)
161 return Write(make_pair(string("watchs"), dest), '1');
164 bool CWalletDB::EraseWatchOnly(const CScript &dest)
167 return Erase(make_pair(string("watchs"), dest));
170 bool CWalletDB::WriteBestBlock(const CBlockLocator& locator)
173 return Write(string("bestblock"), locator);
176 bool CWalletDB::ReadBestBlock(CBlockLocator& locator)
178 return Read(string("bestblock"), locator);
181 bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext)
184 return Write(string("orderposnext"), nOrderPosNext);
187 bool CWalletDB::WriteDefaultKey(const CPubKey& key)
190 return Write(string("defaultkey"), key);
193 bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool)
195 return Read(make_pair(string("pool"), nPool), keypool);
198 bool CWalletDB::WritePool(int64_t nPool, const CKeyPool& keypool)
201 return Write(make_pair(string("pool"), nPool), keypool);
204 bool CWalletDB::ErasePool(int64_t nPool)
207 return Erase(make_pair(string("pool"), nPool));
210 bool CWalletDB::WriteMinVersion(int nVersion)
212 return Write(string("minversion"), nVersion);
215 bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account)
218 return Read(make_pair(string("acc"), strAccount), account);
221 bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account)
223 return Write(make_pair(string("acc"), strAccount), account);
226 bool CWalletDB::WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry)
228 return Write(make_tuple(string("acentry"), acentry.strAccount, nAccEntryNum), acentry);
231 bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry)
233 return WriteAccountingEntry(++nAccountingEntryNumber, acentry);
236 int64_t CWalletDB::GetAccountCreditDebit(const string& strAccount)
238 list<CAccountingEntry> entries;
239 ListAccountCreditDebit(strAccount, entries);
241 int64_t nCreditDebit = 0;
242 for(const auto& entry : entries)
243 nCreditDebit += entry.nCreditDebit;
248 void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountingEntry>& entries)
250 bool fAllAccounts = (strAccount == "*");
252 Dbc* pcursor = GetCursor();
254 throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor");
255 unsigned int fFlags = DB_SET_RANGE;
259 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
260 if (fFlags == DB_SET_RANGE)
261 ssKey << make_tuple(string("acentry"), (fAllAccounts? string("") : strAccount), uint64_t(0));
262 CDataStream ssValue(SER_DISK, CLIENT_VERSION);
263 int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
265 if (ret == DB_NOTFOUND)
270 throw runtime_error("CWalletDB::ListAccountCreditDebit() : error scanning DB");
276 if (strType != "acentry")
278 CAccountingEntry acentry;
279 ssKey >> acentry.strAccount;
280 if (!fAllAccounts && acentry.strAccount != strAccount)
284 ssKey >> acentry.nEntryNo;
285 entries.push_back(acentry);
293 CWalletDB::ReorderTransactions(CWallet* pwallet)
295 LOCK(pwallet->cs_wallet);
296 // Old wallets didn't have any defined order for transactions
297 // Probably a bad idea to change the output of this
299 // First: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap.
300 typedef pair<CWalletTx*, CAccountingEntry*> TxPair;
301 typedef multimap<int64_t, TxPair > TxItems;
304 for (auto it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it)
306 CWalletTx* wtx = &((*it).second);
307 txByTime.insert(make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0)));
309 list<CAccountingEntry> acentries;
310 ListAccountCreditDebit("", acentries);
311 for(auto& entry : acentries)
313 txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry)));
316 int64_t& nOrderPosNext = pwallet->nOrderPosNext;
318 vector<int64_t> nOrderPosOffsets;
319 for (auto it = txByTime.begin(); it != txByTime.end(); ++it)
321 CWalletTx *const pwtx = (*it).second.first;
322 CAccountingEntry *const pacentry = (*it).second.second;
323 int64_t& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos;
327 nOrderPos = nOrderPosNext++;
328 nOrderPosOffsets.push_back(nOrderPos);
331 // Have to write accounting regardless, since we don't keep it in memory
332 if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry))
337 int64_t nOrderPosOff = 0;
338 for(const int64_t& nOffsetStart : nOrderPosOffsets)
340 if (nOrderPos >= nOffsetStart)
343 nOrderPos += nOrderPosOff;
344 nOrderPosNext = max(nOrderPosNext, nOrderPos + 1);
349 // Since we're changing the order, write it back
352 if (!WriteTx(pwtx->GetHash(), *pwtx))
356 if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry))
364 class CWalletScanState {
368 unsigned int nKeyMeta;
372 vector<uint256> vWalletUpgrade;
375 nKeys = nCKeys = nKeyMeta = 0;
376 fIsEncrypted = false;
377 fAnyUnordered = false;
383 ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
384 CWalletScanState &wss, string& strType, string& strErr)
388 // Taking advantage of the fact that pair serialization
389 // is just the two items serialized one after the other
392 if (strType == "name")
396 ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress)];
398 else if (strType == "tx")
402 CWalletTx& wtx = pwallet->mapWallet[hash];
404 if (wtx.CheckTransaction() && (wtx.GetHash() == hash))
405 wtx.BindWallet(pwallet);
408 pwallet->mapWallet.erase(hash);
412 // Undo serialize changes in 31600
413 if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
415 if (!ssValue.empty())
419 ssValue >> fTmp >> fUnused >> wtx.strFromAccount;
420 strErr = strprintf("LoadWallet() upgrading tx ver=%d %d '%s' %s",
421 wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str());
422 wtx.fTimeReceivedIsTxTime = fTmp;
426 strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str());
427 wtx.fTimeReceivedIsTxTime = 0;
429 wss.vWalletUpgrade.push_back(hash);
432 if (wtx.nOrderPos == -1)
433 wss.fAnyUnordered = true;
436 //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str());
437 //printf(" %12"PRId64" %s %s %s\n",
438 // wtx.vout[0].nValue,
439 // DateTimeStrFormat("%x %H:%M:%S", wtx.GetBlockTime()).c_str(),
440 // wtx.hashBlock.ToString().substr(0,20).c_str(),
441 // wtx.mapValue["message"].c_str());
443 else if (strType == "acentry")
449 if (nNumber > nAccountingEntryNumber)
450 nAccountingEntryNumber = nNumber;
452 if (!wss.fAnyUnordered)
454 CAccountingEntry acentry;
456 if (acentry.nOrderPos == -1)
457 wss.fAnyUnordered = true;
460 else if (strType == "watchs")
467 pwallet->LoadWatchOnly(script);
469 // Watch-only addresses have no birthday information for now,
470 // so set the wallet birthday to the beginning of time.
471 pwallet->nTimeFirstKey = 1;
473 else if (strType == "malpair")
479 ssValue >> vchSecret;
481 CMalleableKeyView keyView(strKeyView);
482 if (!pwallet->LoadKey(keyView, vchSecret))
484 strErr = "Error reading wallet database: LoadKey failed";
488 else if (strType == "malcpair")
492 vector<unsigned char> vchCryptedSecret;
494 ssValue >> vchCryptedSecret;
496 CMalleableKeyView keyView(strKeyView);
497 if (!pwallet->LoadCryptedKey(keyView, vchCryptedSecret))
499 strErr = "Error reading wallet database: LoadCryptedKey failed";
503 else if (strType == "key" || strType == "wkey")
508 if (strType == "key")
513 if (!key.SetPrivKey(pkey))
515 strErr = "Error reading wallet database: CPrivKey corrupt";
518 if (key.GetPubKey() != vchPubKey)
520 strErr = "Error reading wallet database: CPrivKey pubkey inconsistency";
523 key.SetCompressedPubKey(vchPubKey.IsCompressed());
526 strErr = "Error reading wallet database: invalid CPrivKey";
534 if (!key.SetPrivKey(wkey.vchPrivKey))
536 strErr = "Error reading wallet database: CPrivKey corrupt";
539 if (key.GetPubKey() != vchPubKey)
541 strErr = "Error reading wallet database: CWalletKey pubkey inconsistency";
544 key.SetCompressedPubKey(vchPubKey.IsCompressed());
547 strErr = "Error reading wallet database: invalid CWalletKey";
551 if (!pwallet->LoadKey(key))
553 strErr = "Error reading wallet database: LoadKey failed";
557 else if (strType == "mkey")
561 CMasterKey kMasterKey;
562 ssValue >> kMasterKey;
564 if(pwallet->mapMasterKeys.count(nID) != 0)
566 strErr = strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID);
569 pwallet->mapMasterKeys[nID] = kMasterKey;
570 if (pwallet->nMasterKeyMaxID < nID)
571 pwallet->nMasterKeyMaxID = nID;
573 else if (strType == "ckey")
578 vector<unsigned char> vchPrivKey;
579 ssValue >> vchPrivKey;
580 if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey))
582 strErr = "Error reading wallet database: LoadCryptedKey failed";
585 wss.fIsEncrypted = true;
587 else if (strType == "malmeta")
592 CMalleableKeyView keyView;
593 keyView.SetString(strKeyView);
595 CKeyMetadata keyMeta;
599 pwallet->LoadKeyMetadata(keyView, keyMeta);
601 else if (strType == "keymeta")
605 CKeyMetadata keyMeta;
609 pwallet->LoadKeyMetadata(vchPubKey, keyMeta);
611 // find earliest key creation time, as wallet birthday
612 if (!pwallet->nTimeFirstKey ||
613 (keyMeta.nCreateTime < pwallet->nTimeFirstKey))
614 pwallet->nTimeFirstKey = keyMeta.nCreateTime;
616 else if (strType == "defaultkey")
618 ssValue >> pwallet->vchDefaultKey;
620 else if (strType == "pool")
626 pwallet->setKeyPool.insert(nIndex);
628 // If no metadata exists yet, create a default with the pool key's
629 // creation time. Note that this may be overwritten by actually
630 // stored metadata for that key later, which is fine.
631 CBitcoinAddress addr = CBitcoinAddress(keypool.vchPubKey.GetID());
632 if (pwallet->mapKeyMetadata.count(addr) == 0)
633 pwallet->mapKeyMetadata[addr] = CKeyMetadata(keypool.nTime);
636 else if (strType == "version")
638 ssValue >> wss.nFileVersion;
639 if (wss.nFileVersion == 10300)
640 wss.nFileVersion = 300;
642 else if (strType == "cscript")
648 if (!pwallet->LoadCScript(script))
650 strErr = "Error reading wallet database: LoadCScript failed";
654 else if (strType == "orderposnext")
656 ssValue >> pwallet->nOrderPosNext;
665 static bool IsKeyType(string strType)
667 return (strType== "key" || strType == "wkey" ||
668 strType == "mkey" || strType == "ckey" || strType == "malpair" || strType == "malcpair");
671 DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
673 pwallet->vchDefaultKey = CPubKey();
674 CWalletScanState wss;
675 bool fNoncriticalErrors = false;
676 DBErrors result = DB_LOAD_OK;
679 LOCK(pwallet->cs_wallet);
681 if (Read((string)"minversion", nMinVersion))
683 if (nMinVersion > CLIENT_VERSION)
685 pwallet->LoadMinVersion(nMinVersion);
689 Dbc* pcursor = GetCursor();
692 printf("Error getting wallet database cursor\n");
699 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
700 CDataStream ssValue(SER_DISK, CLIENT_VERSION);
701 int ret = ReadAtCursor(pcursor, ssKey, ssValue);
702 if (ret == DB_NOTFOUND)
706 printf("Error reading next record from wallet database\n");
710 // Try to be tolerant of single corrupt records:
711 string strType, strErr;
712 if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr))
714 // losing keys is considered a catastrophic error, anything else
715 // we assume the user can live with:
716 if (IsKeyType(strType))
720 // Leave other errors alone, if we try to fix them we might make things worse.
721 fNoncriticalErrors = true; // ... but do warn the user there is something wrong.
723 // Rescan if there is a bad transaction record:
724 SoftSetBoolArg("-rescan", true);
728 printf("%s\n", strErr.c_str());
737 if (fNoncriticalErrors && result == DB_LOAD_OK)
738 result = DB_NONCRITICAL_ERROR;
740 // Any wallet corruption at all: skip any rewriting or
741 // upgrading, we don't want to make it worse.
742 if (result != DB_LOAD_OK)
745 printf("nFileVersion = %d\n", wss.nFileVersion);
747 printf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total\n",
748 wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys);
750 // nTimeFirstKey is only reliable if all keys have metadata
751 if ((wss.nKeys + wss.nCKeys) != wss.nKeyMeta)
752 pwallet->nTimeFirstKey = 1; // 0 would be considered 'no value'
755 for(uint256 hash : wss.vWalletUpgrade)
756 WriteTx(hash, pwallet->mapWallet[hash]);
758 // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
759 if (wss.fIsEncrypted && (wss.nFileVersion == 40000 || wss.nFileVersion == 50000))
760 return DB_NEED_REWRITE;
762 if (wss.nFileVersion < CLIENT_VERSION) // Update
763 WriteVersion(CLIENT_VERSION);
765 if (wss.fAnyUnordered)
766 result = ReorderTransactions(pwallet);
771 DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& vTxHash)
773 pwallet->vchDefaultKey = CPubKey();
774 CWalletScanState wss;
775 bool fNoncriticalErrors = false;
776 DBErrors result = DB_LOAD_OK;
779 LOCK(pwallet->cs_wallet);
781 if (Read((string)"minversion", nMinVersion))
783 if (nMinVersion > CLIENT_VERSION)
785 pwallet->LoadMinVersion(nMinVersion);
789 Dbc* pcursor = GetCursor();
792 printf("Error getting wallet database cursor\n");
799 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
800 CDataStream ssValue(SER_DISK, CLIENT_VERSION);
801 int ret = ReadAtCursor(pcursor, ssKey, ssValue);
802 if (ret == DB_NOTFOUND)
806 printf("Error reading next record from wallet database\n");
812 if (strType == "tx") {
816 vTxHash.push_back(hash);
821 catch (const boost::thread_interrupted&) {
828 if (fNoncriticalErrors && result == DB_LOAD_OK)
829 result = DB_NONCRITICAL_ERROR;
834 DBErrors CWalletDB::ZapWalletTx(CWallet* pwallet)
836 // build list of wallet TXs
837 vector<uint256> vTxHash;
838 DBErrors err = FindWalletTx(pwallet, vTxHash);
839 if (err != DB_LOAD_OK)
842 // erase each wallet TX
843 for(uint256& hash : vTxHash) {
851 void ThreadFlushWalletDB(void* parg)
853 // Make this thread recognisable as the wallet flushing thread
854 RenameThread("novacoin-wallet");
856 const string& strFile = ((const string*)parg)[0];
857 static bool fOneThread;
861 if (!GetBoolArg("-flushwallet", true))
864 unsigned int nLastSeen = nWalletDBUpdated;
865 unsigned int nLastFlushed = nWalletDBUpdated;
866 int64_t nLastWalletUpdate = GetTime();
871 if (nLastSeen != nWalletDBUpdated)
873 nLastSeen = nWalletDBUpdated;
874 nLastWalletUpdate = GetTime();
877 if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2)
879 TRY_LOCK(bitdb.cs_db,lockDb);
882 // Don't do this if any databases are in use
884 auto mi = bitdb.mapFileUseCount.begin();
885 while (mi != bitdb.mapFileUseCount.end())
887 nRefCount += (*mi).second;
891 if (nRefCount == 0 && !fShutdown)
893 auto mi = bitdb.mapFileUseCount.find(strFile);
894 if (mi != bitdb.mapFileUseCount.end())
896 printf("Flushing wallet.dat\n");
897 nLastFlushed = nWalletDBUpdated;
898 int64_t nStart = GetTimeMillis();
900 // Flush wallet.dat so it's self contained
901 bitdb.CloseDb(strFile);
902 bitdb.CheckpointLSN(strFile);
904 bitdb.mapFileUseCount.erase(mi++);
905 printf("Flushed wallet.dat %" PRId64 "ms\n", GetTimeMillis() - nStart);
913 bool BackupWallet(const CWallet& wallet, const string& strDest)
915 if (!wallet.fFileBacked)
921 if (!bitdb.mapFileUseCount.count(wallet.strWalletFile) || bitdb.mapFileUseCount[wallet.strWalletFile] == 0)
923 // Flush log data to the dat file
924 bitdb.CloseDb(wallet.strWalletFile);
925 bitdb.CheckpointLSN(wallet.strWalletFile);
926 bitdb.mapFileUseCount.erase(wallet.strWalletFile);
929 boost::filesystem::path pathSrc = GetDataDir() / wallet.strWalletFile;
930 boost::filesystem::path pathDest(strDest);
931 if (boost::filesystem::is_directory(pathDest))
932 pathDest /= wallet.strWalletFile;
935 #if BOOST_VERSION >= 104000
936 boost::filesystem::copy_file(pathSrc, pathDest, boost::filesystem::copy_option::overwrite_if_exists);
938 boost::filesystem::copy_file(pathSrc, pathDest);
940 printf("copied wallet.dat to %s\n", pathDest.string().c_str());
942 } catch(const boost::filesystem::filesystem_error &e) {
943 printf("error copying wallet.dat to %s - %s\n", pathDest.string().c_str(), e.what());
953 bool DumpWallet(CWallet* pwallet, const string& strDest)
955 if (!pwallet->fFileBacked)
958 map<CBitcoinAddress, int64_t> mapAddresses;
959 set<CKeyID> setKeyPool;
961 pwallet->GetAddresses(mapAddresses);
962 pwallet->GetAllReserveKeys(setKeyPool);
964 // sort time/key pairs
965 vector<pair<int64_t, CBitcoinAddress> > vAddresses;
966 for (auto it = mapAddresses.begin(); it != mapAddresses.end(); it++) {
967 vAddresses.push_back({ it->second, it->first });
969 mapAddresses.clear();
970 sort(vAddresses.begin(), vAddresses.end());
972 // open outputfile as a stream
974 file.open(strDest.c_str());
979 file << strprintf("# Wallet dump created by NovaCoin %s (%s)\n", CLIENT_BUILD.c_str(), CLIENT_DATE.c_str());
980 file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime()).c_str());
981 file << strprintf("# * Best block at time of backup was %i (%s),\n", nBestHeight, hashBestChain.ToString().c_str());
982 file << strprintf("# mined on %s\n", EncodeDumpTime(pindexBest->nTime).c_str());
985 for (const auto& addrItem : vAddresses) {
986 const auto &addr = addrItem.second;
987 const auto strTime = EncodeDumpTime(addrItem.first);
988 const auto strAddr = addr.ToString();
991 // Pubkey pair address
992 CMalleableKeyView keyView;
993 CMalleablePubKey mPubKey(addr.GetData());
994 if (!pwallet->GetMalleableView(mPubKey, keyView))
997 pwallet->GetMalleableKey(keyView, mKey);
998 file << mKey.ToString();
999 if (pwallet->mapAddressBook.count(addr))
1000 file << strprintf(" %s label=%s # view=%s addr=%s\n", strTime.c_str(), EncodeDumpString(pwallet->mapAddressBook[addr]).c_str(), keyView.ToString().c_str(), strAddr.c_str());
1002 file << strprintf(" %s # view=%s addr=%s\n", strTime.c_str(), keyView.ToString().c_str(), strAddr.c_str());
1005 // Pubkey hash address
1007 addr.GetKeyID(keyid);
1010 if (!pwallet->GetKey(keyid, key))
1012 auto secret = key.GetSecret(IsCompressed);
1013 file << CBitcoinSecret(secret, IsCompressed).ToString();
1014 if (pwallet->mapAddressBook.count(addr))
1015 file << strprintf(" %s label=%s # addr=%s\n", strTime.c_str(), EncodeDumpString(pwallet->mapAddressBook[addr]).c_str(), strAddr.c_str());
1016 else if (setKeyPool.count(keyid))
1017 file << strprintf(" %s reserve=1 # addr=%s\n", strTime.c_str(), strAddr.c_str());
1019 file << strprintf(" %s change=1 # addr=%s\n", strTime.c_str(), strAddr.c_str());
1024 file << "# End of dump\n";
1030 bool ImportWallet(CWallet *pwallet, const string& strLocation)
1033 if (!pwallet->fFileBacked)
1036 // open inputfile as stream
1038 file.open(strLocation.c_str());
1039 if (!file.is_open())
1043 int64_t nTimeBegin = pindexBest->nTime;
1045 // read through input file checking and importing keys into wallet.
1046 while (file.good()) {
1048 getline(file, line);
1049 if (line.empty() || line[0] == '#')
1050 continue; // Skip comments and empty lines
1052 vector<string> vstr;
1053 istringstream iss(line);
1054 copy(istream_iterator<string>(iss), istream_iterator<string>(), back_inserter(vstr));
1055 if (vstr.size() < 2)
1058 int64_t nTime = DecodeDumpTime(vstr[1]);
1061 for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) {
1062 if (vstr[nStr].compare(0,1, "#") == 0)
1064 if (vstr[nStr] == "change=1")
1066 if (vstr[nStr] == "reserve=1")
1068 if (vstr[nStr].compare(0,6, "label=") == 0) {
1069 strLabel = DecodeDumpString(vstr[nStr].substr(6));
1074 CBitcoinAddress addr;
1075 CBitcoinSecret vchSecret;
1076 if (vchSecret.SetString(vstr[0])) {
1077 // Simple private key
1081 auto secret = vchSecret.GetSecret(fCompressed);
1082 key.SetSecret(secret, fCompressed);
1083 auto keyid = key.GetPubKey().GetID();
1084 addr = CBitcoinAddress(keyid);
1086 if (pwallet->HaveKey(keyid)) {
1087 printf("Skipping import of %s (key already present)\n", addr.ToString().c_str());
1091 printf("Importing %s...\n", addr.ToString().c_str());
1092 if (!pwallet->AddKey(key)) {
1097 // A pair of private keys
1100 if (!mKey.SetString(vstr[0]))
1102 auto mPubKey = mKey.GetMalleablePubKey();
1103 addr = CBitcoinAddress(mPubKey);
1105 if (pwallet->CheckOwnership(mPubKey)) {
1106 printf("Skipping import of %s (key already present)\n", addr.ToString().c_str());
1110 printf("Importing %s...\n", addr.ToString().c_str());
1111 if (!pwallet->AddKey(mKey)) {
1117 pwallet->mapKeyMetadata[addr].nCreateTime = nTime;
1119 pwallet->SetAddressBookName(addr, strLabel);
1121 nTimeBegin = min(nTimeBegin, nTime);
1125 // rescan block chain looking for coins from new keys
1126 CBlockIndex *pindex = pindexBest;
1127 while (pindex && pindex->pprev && pindex->nTime > nTimeBegin - 7200)
1128 pindex = pindex->pprev;
1130 printf("Rescanning last %i blocks\n", pindexBest->nHeight - pindex->nHeight + 1);
1131 pwallet->ScanForWalletTransactions(pindex);
1132 pwallet->ReacceptWalletTransactions();
1133 pwallet->MarkDirty();
1139 // Try to (very carefully!) recover wallet.dat if there is a problem.
1141 bool CWalletDB::Recover(CDBEnv& dbenv, string filename, bool fOnlyKeys)
1143 // Recovery procedure:
1144 // move wallet.dat to wallet.timestamp.bak
1145 // Call Salvage with fAggressive=true to
1146 // get as much data as possible.
1147 // Rewrite salvaged data to wallet.dat
1148 // Set -rescan so any missing transactions will be
1150 int64_t now = GetTime();
1151 string newFilename = strprintf("wallet.%" PRId64 ".bak", now);
1153 int result = dbenv.dbenv.dbrename(NULL, filename.c_str(), NULL,
1154 newFilename.c_str(), DB_AUTO_COMMIT);
1156 printf("Renamed %s to %s\n", filename.c_str(), newFilename.c_str());
1159 printf("Failed to rename %s to %s\n", filename.c_str(), newFilename.c_str());
1163 vector<CDBEnv::KeyValPair> salvagedData;
1164 bool allOK = dbenv.Salvage(newFilename, true, salvagedData);
1165 if (salvagedData.empty())
1167 printf("Salvage(aggressive) found no records in %s.\n", newFilename.c_str());
1170 printf("Salvage(aggressive) found %" PRIszu " records\n", salvagedData.size());
1172 bool fSuccess = allOK;
1173 unique_ptr<Db> pdbCopy(new Db(&dbenv.dbenv, 0));
1174 int ret = pdbCopy->open(NULL, // Txn pointer
1175 filename.c_str(), // Filename
1176 "main", // Logical db name
1177 DB_BTREE, // Database type
1182 printf("Cannot create database file %s\n", filename.c_str());
1185 CWallet dummyWallet;
1186 CWalletScanState wss;
1188 DbTxn* ptxn = dbenv.TxnBegin();
1189 for(CDBEnv::KeyValPair& row : salvagedData)
1193 CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION);
1194 CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION);
1195 string strType, strErr;
1196 bool fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue,
1197 wss, strType, strErr);
1198 if (!IsKeyType(strType))
1202 printf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType.c_str(), strErr.c_str());
1206 Dbt datKey(&row.first[0], row.first.size());
1207 Dbt datValue(&row.second[0], row.second.size());
1208 int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
1218 bool CWalletDB::Recover(CDBEnv& dbenv, string filename)
1220 return CWalletDB::Recover(dbenv, filename, false);