diff options
author | Jonas Schnelli <jonas.schnelli@include7.ch> | 2015-02-03 21:09:47 +0100 |
---|---|---|
committer | Jonas Schnelli <jonas.schnelli@include7.ch> | 2015-03-12 14:13:02 +0100 |
commit | 50c72f23ad5f7fcd13bf016f79cac6323c329caf (patch) | |
tree | 2ea49700ab27d3e9705f7a035f1a5783beed4ca2 /src/walletdb.cpp | |
parent | e564e63ef04e55b1e446f8440d51c611bc41cec6 (diff) |
[Move Only] Move wallet related things to src/wallet/
could once be renamed from /src/wallet to /src/legacywallet.
Diffstat (limited to 'src/walletdb.cpp')
-rw-r--r-- | src/walletdb.cpp | 986 |
1 files changed, 0 insertions, 986 deletions
diff --git a/src/walletdb.cpp b/src/walletdb.cpp deleted file mode 100644 index ddec57d9a9..0000000000 --- a/src/walletdb.cpp +++ /dev/null @@ -1,986 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "walletdb.h" - -#include "base58.h" -#include "protocol.h" -#include "serialize.h" -#include "sync.h" -#include "util.h" -#include "utiltime.h" -#include "wallet.h" - -#include <boost/filesystem.hpp> -#include <boost/foreach.hpp> -#include <boost/scoped_ptr.hpp> -#include <boost/thread.hpp> - -using namespace std; - -static uint64_t nAccountingEntryNumber = 0; - -// -// CWalletDB -// - -bool CWalletDB::WriteName(const string& strAddress, const string& strName) -{ - nWalletDBUpdated++; - return Write(make_pair(string("name"), strAddress), strName); -} - -bool CWalletDB::EraseName(const string& strAddress) -{ - // This should only be used for sending addresses, never for receiving addresses, - // receiving addresses must always have an address book entry if they're not change return. - nWalletDBUpdated++; - return Erase(make_pair(string("name"), strAddress)); -} - -bool CWalletDB::WritePurpose(const string& strAddress, const string& strPurpose) -{ - nWalletDBUpdated++; - return Write(make_pair(string("purpose"), strAddress), strPurpose); -} - -bool CWalletDB::ErasePurpose(const string& strPurpose) -{ - nWalletDBUpdated++; - return Erase(make_pair(string("purpose"), strPurpose)); -} - -bool CWalletDB::WriteTx(uint256 hash, const CWalletTx& wtx) -{ - nWalletDBUpdated++; - return Write(std::make_pair(std::string("tx"), hash), wtx); -} - -bool CWalletDB::EraseTx(uint256 hash) -{ - nWalletDBUpdated++; - return Erase(std::make_pair(std::string("tx"), hash)); -} - -bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta) -{ - nWalletDBUpdated++; - - if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), - keyMeta, false)) - return false; - - // hash pubkey/privkey to accelerate wallet load - std::vector<unsigned char> vchKey; - vchKey.reserve(vchPubKey.size() + vchPrivKey.size()); - vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end()); - vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end()); - - return Write(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false); -} - -bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey, - const std::vector<unsigned char>& vchCryptedSecret, - const CKeyMetadata &keyMeta) -{ - const bool fEraseUnencryptedKey = true; - nWalletDBUpdated++; - - if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), - keyMeta)) - return false; - - if (!Write(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false)) - return false; - if (fEraseUnencryptedKey) - { - Erase(std::make_pair(std::string("key"), vchPubKey)); - Erase(std::make_pair(std::string("wkey"), vchPubKey)); - } - return true; -} - -bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey) -{ - nWalletDBUpdated++; - return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true); -} - -bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript) -{ - nWalletDBUpdated++; - return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false); -} - -bool CWalletDB::WriteWatchOnly(const CScript &dest) -{ - nWalletDBUpdated++; - return Write(std::make_pair(std::string("watchs"), dest), '1'); -} - -bool CWalletDB::EraseWatchOnly(const CScript &dest) -{ - nWalletDBUpdated++; - return Erase(std::make_pair(std::string("watchs"), dest)); -} - -bool CWalletDB::WriteBestBlock(const CBlockLocator& locator) -{ - nWalletDBUpdated++; - return Write(std::string("bestblock"), locator); -} - -bool CWalletDB::ReadBestBlock(CBlockLocator& locator) -{ - return Read(std::string("bestblock"), locator); -} - -bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext) -{ - nWalletDBUpdated++; - return Write(std::string("orderposnext"), nOrderPosNext); -} - -bool CWalletDB::WriteDefaultKey(const CPubKey& vchPubKey) -{ - nWalletDBUpdated++; - return Write(std::string("defaultkey"), vchPubKey); -} - -bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool) -{ - return Read(std::make_pair(std::string("pool"), nPool), keypool); -} - -bool CWalletDB::WritePool(int64_t nPool, const CKeyPool& keypool) -{ - nWalletDBUpdated++; - return Write(std::make_pair(std::string("pool"), nPool), keypool); -} - -bool CWalletDB::ErasePool(int64_t nPool) -{ - nWalletDBUpdated++; - return Erase(std::make_pair(std::string("pool"), nPool)); -} - -bool CWalletDB::WriteMinVersion(int nVersion) -{ - return Write(std::string("minversion"), nVersion); -} - -bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account) -{ - account.SetNull(); - return Read(make_pair(string("acc"), strAccount), account); -} - -bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account) -{ - return Write(make_pair(string("acc"), strAccount), account); -} - -bool CWalletDB::WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry) -{ - return Write(std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry); -} - -bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry) -{ - return WriteAccountingEntry(++nAccountingEntryNumber, acentry); -} - -CAmount CWalletDB::GetAccountCreditDebit(const string& strAccount) -{ - list<CAccountingEntry> entries; - ListAccountCreditDebit(strAccount, entries); - - CAmount nCreditDebit = 0; - BOOST_FOREACH (const CAccountingEntry& entry, entries) - nCreditDebit += entry.nCreditDebit; - - return nCreditDebit; -} - -void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountingEntry>& entries) -{ - bool fAllAccounts = (strAccount == "*"); - - Dbc* pcursor = GetCursor(); - if (!pcursor) - throw runtime_error("CWalletDB::ListAccountCreditDebit(): cannot create DB cursor"); - unsigned int fFlags = DB_SET_RANGE; - while (true) - { - // Read next record - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - if (fFlags == DB_SET_RANGE) - ssKey << std::make_pair(std::string("acentry"), std::make_pair((fAllAccounts ? string("") : strAccount), uint64_t(0))); - CDataStream ssValue(SER_DISK, CLIENT_VERSION); - int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); - fFlags = DB_NEXT; - if (ret == DB_NOTFOUND) - break; - else if (ret != 0) - { - pcursor->close(); - throw runtime_error("CWalletDB::ListAccountCreditDebit(): error scanning DB"); - } - - // Unserialize - string strType; - ssKey >> strType; - if (strType != "acentry") - break; - CAccountingEntry acentry; - ssKey >> acentry.strAccount; - if (!fAllAccounts && acentry.strAccount != strAccount) - break; - - ssValue >> acentry; - ssKey >> acentry.nEntryNo; - entries.push_back(acentry); - } - - pcursor->close(); -} - -DBErrors CWalletDB::ReorderTransactions(CWallet* pwallet) -{ - LOCK(pwallet->cs_wallet); - // Old wallets didn't have any defined order for transactions - // Probably a bad idea to change the output of this - - // First: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap. - typedef pair<CWalletTx*, CAccountingEntry*> TxPair; - typedef multimap<int64_t, TxPair > TxItems; - TxItems txByTime; - - for (map<uint256, CWalletTx>::iterator it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it) - { - CWalletTx* wtx = &((*it).second); - txByTime.insert(make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0))); - } - list<CAccountingEntry> acentries; - ListAccountCreditDebit("", acentries); - BOOST_FOREACH(CAccountingEntry& entry, acentries) - { - txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry))); - } - - int64_t& nOrderPosNext = pwallet->nOrderPosNext; - nOrderPosNext = 0; - std::vector<int64_t> nOrderPosOffsets; - for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it) - { - CWalletTx *const pwtx = (*it).second.first; - CAccountingEntry *const pacentry = (*it).second.second; - int64_t& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos; - - if (nOrderPos == -1) - { - nOrderPos = nOrderPosNext++; - nOrderPosOffsets.push_back(nOrderPos); - - if (pwtx) - { - if (!WriteTx(pwtx->GetHash(), *pwtx)) - return DB_LOAD_FAIL; - } - else - if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) - return DB_LOAD_FAIL; - } - else - { - int64_t nOrderPosOff = 0; - BOOST_FOREACH(const int64_t& nOffsetStart, nOrderPosOffsets) - { - if (nOrderPos >= nOffsetStart) - ++nOrderPosOff; - } - nOrderPos += nOrderPosOff; - nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1); - - if (!nOrderPosOff) - continue; - - // Since we're changing the order, write it back - if (pwtx) - { - if (!WriteTx(pwtx->GetHash(), *pwtx)) - return DB_LOAD_FAIL; - } - else - if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) - return DB_LOAD_FAIL; - } - } - WriteOrderPosNext(nOrderPosNext); - - return DB_LOAD_OK; -} - -class CWalletScanState { -public: - unsigned int nKeys; - unsigned int nCKeys; - unsigned int nKeyMeta; - bool fIsEncrypted; - bool fAnyUnordered; - int nFileVersion; - vector<uint256> vWalletUpgrade; - - CWalletScanState() { - nKeys = nCKeys = nKeyMeta = 0; - fIsEncrypted = false; - fAnyUnordered = false; - nFileVersion = 0; - } -}; - -bool -ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, - CWalletScanState &wss, string& strType, string& strErr) -{ - try { - // Unserialize - // Taking advantage of the fact that pair serialization - // is just the two items serialized one after the other - ssKey >> strType; - if (strType == "name") - { - string strAddress; - ssKey >> strAddress; - ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].name; - } - else if (strType == "purpose") - { - string strAddress; - ssKey >> strAddress; - ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].purpose; - } - else if (strType == "tx") - { - uint256 hash; - ssKey >> hash; - CWalletTx wtx; - ssValue >> wtx; - CValidationState state; - if (!(CheckTransaction(wtx, state) && (wtx.GetHash() == hash) && state.IsValid())) - return false; - - // Undo serialize changes in 31600 - if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703) - { - if (!ssValue.empty()) - { - char fTmp; - char fUnused; - ssValue >> fTmp >> fUnused >> wtx.strFromAccount; - strErr = strprintf("LoadWallet() upgrading tx ver=%d %d '%s' %s", - wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount, hash.ToString()); - wtx.fTimeReceivedIsTxTime = fTmp; - } - else - { - strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString()); - wtx.fTimeReceivedIsTxTime = 0; - } - wss.vWalletUpgrade.push_back(hash); - } - - if (wtx.nOrderPos == -1) - wss.fAnyUnordered = true; - - pwallet->AddToWallet(wtx, true, NULL); - } - else if (strType == "acentry") - { - string strAccount; - ssKey >> strAccount; - uint64_t nNumber; - ssKey >> nNumber; - if (nNumber > nAccountingEntryNumber) - nAccountingEntryNumber = nNumber; - - if (!wss.fAnyUnordered) - { - CAccountingEntry acentry; - ssValue >> acentry; - if (acentry.nOrderPos == -1) - wss.fAnyUnordered = true; - } - } - else if (strType == "watchs") - { - CScript script; - ssKey >> script; - char fYes; - ssValue >> fYes; - if (fYes == '1') - pwallet->LoadWatchOnly(script); - - // Watch-only addresses have no birthday information for now, - // so set the wallet birthday to the beginning of time. - pwallet->nTimeFirstKey = 1; - } - else if (strType == "key" || strType == "wkey") - { - CPubKey vchPubKey; - ssKey >> vchPubKey; - if (!vchPubKey.IsValid()) - { - strErr = "Error reading wallet database: CPubKey corrupt"; - return false; - } - CKey key; - CPrivKey pkey; - uint256 hash; - - if (strType == "key") - { - wss.nKeys++; - ssValue >> pkey; - } else { - CWalletKey wkey; - ssValue >> wkey; - pkey = wkey.vchPrivKey; - } - - // Old wallets store keys as "key" [pubkey] => [privkey] - // ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key - // using EC operations as a checksum. - // Newer wallets store keys as "key"[pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while - // remaining backwards-compatible. - try - { - ssValue >> hash; - } - catch (...) {} - - bool fSkipCheck = false; - - if (!hash.IsNull()) - { - // hash pubkey/privkey to accelerate wallet load - std::vector<unsigned char> vchKey; - vchKey.reserve(vchPubKey.size() + pkey.size()); - vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end()); - vchKey.insert(vchKey.end(), pkey.begin(), pkey.end()); - - if (Hash(vchKey.begin(), vchKey.end()) != hash) - { - strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt"; - return false; - } - - fSkipCheck = true; - } - - if (!key.Load(pkey, vchPubKey, fSkipCheck)) - { - strErr = "Error reading wallet database: CPrivKey corrupt"; - return false; - } - if (!pwallet->LoadKey(key, vchPubKey)) - { - strErr = "Error reading wallet database: LoadKey failed"; - return false; - } - } - else if (strType == "mkey") - { - unsigned int nID; - ssKey >> nID; - CMasterKey kMasterKey; - ssValue >> kMasterKey; - if(pwallet->mapMasterKeys.count(nID) != 0) - { - strErr = strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID); - return false; - } - pwallet->mapMasterKeys[nID] = kMasterKey; - if (pwallet->nMasterKeyMaxID < nID) - pwallet->nMasterKeyMaxID = nID; - } - else if (strType == "ckey") - { - vector<unsigned char> vchPubKey; - ssKey >> vchPubKey; - vector<unsigned char> vchPrivKey; - ssValue >> vchPrivKey; - wss.nCKeys++; - - if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey)) - { - strErr = "Error reading wallet database: LoadCryptedKey failed"; - return false; - } - wss.fIsEncrypted = true; - } - else if (strType == "keymeta") - { - CPubKey vchPubKey; - ssKey >> vchPubKey; - CKeyMetadata keyMeta; - ssValue >> keyMeta; - wss.nKeyMeta++; - - pwallet->LoadKeyMetadata(vchPubKey, keyMeta); - - // find earliest key creation time, as wallet birthday - if (!pwallet->nTimeFirstKey || - (keyMeta.nCreateTime < pwallet->nTimeFirstKey)) - pwallet->nTimeFirstKey = keyMeta.nCreateTime; - } - else if (strType == "defaultkey") - { - ssValue >> pwallet->vchDefaultKey; - } - else if (strType == "pool") - { - int64_t nIndex; - ssKey >> nIndex; - CKeyPool keypool; - ssValue >> keypool; - pwallet->setKeyPool.insert(nIndex); - - // If no metadata exists yet, create a default with the pool key's - // creation time. Note that this may be overwritten by actually - // stored metadata for that key later, which is fine. - CKeyID keyid = keypool.vchPubKey.GetID(); - if (pwallet->mapKeyMetadata.count(keyid) == 0) - pwallet->mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime); - } - else if (strType == "version") - { - ssValue >> wss.nFileVersion; - if (wss.nFileVersion == 10300) - wss.nFileVersion = 300; - } - else if (strType == "cscript") - { - uint160 hash; - ssKey >> hash; - CScript script; - ssValue >> script; - if (!pwallet->LoadCScript(script)) - { - strErr = "Error reading wallet database: LoadCScript failed"; - return false; - } - } - else if (strType == "orderposnext") - { - ssValue >> pwallet->nOrderPosNext; - } - else if (strType == "destdata") - { - std::string strAddress, strKey, strValue; - ssKey >> strAddress; - ssKey >> strKey; - ssValue >> strValue; - if (!pwallet->LoadDestData(CBitcoinAddress(strAddress).Get(), strKey, strValue)) - { - strErr = "Error reading wallet database: LoadDestData failed"; - return false; - } - } - } catch (...) - { - return false; - } - return true; -} - -static bool IsKeyType(string strType) -{ - return (strType== "key" || strType == "wkey" || - strType == "mkey" || strType == "ckey"); -} - -DBErrors CWalletDB::LoadWallet(CWallet* pwallet) -{ - pwallet->vchDefaultKey = CPubKey(); - CWalletScanState wss; - bool fNoncriticalErrors = false; - DBErrors result = DB_LOAD_OK; - - try { - LOCK(pwallet->cs_wallet); - int nMinVersion = 0; - if (Read((string)"minversion", nMinVersion)) - { - if (nMinVersion > CLIENT_VERSION) - return DB_TOO_NEW; - pwallet->LoadMinVersion(nMinVersion); - } - - // Get cursor - Dbc* pcursor = GetCursor(); - if (!pcursor) - { - LogPrintf("Error getting wallet database cursor\n"); - return DB_CORRUPT; - } - - while (true) - { - // Read next record - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - CDataStream ssValue(SER_DISK, CLIENT_VERSION); - int ret = ReadAtCursor(pcursor, ssKey, ssValue); - if (ret == DB_NOTFOUND) - break; - else if (ret != 0) - { - LogPrintf("Error reading next record from wallet database\n"); - return DB_CORRUPT; - } - - // Try to be tolerant of single corrupt records: - string strType, strErr; - if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr)) - { - // losing keys is considered a catastrophic error, anything else - // we assume the user can live with: - if (IsKeyType(strType)) - result = DB_CORRUPT; - else - { - // Leave other errors alone, if we try to fix them we might make things worse. - fNoncriticalErrors = true; // ... but do warn the user there is something wrong. - if (strType == "tx") - // Rescan if there is a bad transaction record: - SoftSetBoolArg("-rescan", true); - } - } - if (!strErr.empty()) - LogPrintf("%s\n", strErr); - } - pcursor->close(); - } - catch (const boost::thread_interrupted&) { - throw; - } - catch (...) { - result = DB_CORRUPT; - } - - if (fNoncriticalErrors && result == DB_LOAD_OK) - result = DB_NONCRITICAL_ERROR; - - // Any wallet corruption at all: skip any rewriting or - // upgrading, we don't want to make it worse. - if (result != DB_LOAD_OK) - return result; - - LogPrintf("nFileVersion = %d\n", wss.nFileVersion); - - LogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total\n", - wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys); - - // nTimeFirstKey is only reliable if all keys have metadata - if ((wss.nKeys + wss.nCKeys) != wss.nKeyMeta) - pwallet->nTimeFirstKey = 1; // 0 would be considered 'no value' - - BOOST_FOREACH(uint256 hash, wss.vWalletUpgrade) - WriteTx(hash, pwallet->mapWallet[hash]); - - // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: - if (wss.fIsEncrypted && (wss.nFileVersion == 40000 || wss.nFileVersion == 50000)) - return DB_NEED_REWRITE; - - if (wss.nFileVersion < CLIENT_VERSION) // Update - WriteVersion(CLIENT_VERSION); - - if (wss.fAnyUnordered) - result = ReorderTransactions(pwallet); - - return result; -} - -DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& vTxHash, vector<CWalletTx>& vWtx) -{ - pwallet->vchDefaultKey = CPubKey(); - bool fNoncriticalErrors = false; - DBErrors result = DB_LOAD_OK; - - try { - LOCK(pwallet->cs_wallet); - int nMinVersion = 0; - if (Read((string)"minversion", nMinVersion)) - { - if (nMinVersion > CLIENT_VERSION) - return DB_TOO_NEW; - pwallet->LoadMinVersion(nMinVersion); - } - - // Get cursor - Dbc* pcursor = GetCursor(); - if (!pcursor) - { - LogPrintf("Error getting wallet database cursor\n"); - return DB_CORRUPT; - } - - while (true) - { - // Read next record - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - CDataStream ssValue(SER_DISK, CLIENT_VERSION); - int ret = ReadAtCursor(pcursor, ssKey, ssValue); - if (ret == DB_NOTFOUND) - break; - else if (ret != 0) - { - LogPrintf("Error reading next record from wallet database\n"); - return DB_CORRUPT; - } - - string strType; - ssKey >> strType; - if (strType == "tx") { - uint256 hash; - ssKey >> hash; - - CWalletTx wtx; - ssValue >> wtx; - - vTxHash.push_back(hash); - vWtx.push_back(wtx); - } - } - pcursor->close(); - } - catch (const boost::thread_interrupted&) { - throw; - } - catch (...) { - result = DB_CORRUPT; - } - - if (fNoncriticalErrors && result == DB_LOAD_OK) - result = DB_NONCRITICAL_ERROR; - - return result; -} - -DBErrors CWalletDB::ZapWalletTx(CWallet* pwallet, vector<CWalletTx>& vWtx) -{ - // build list of wallet TXs - vector<uint256> vTxHash; - DBErrors err = FindWalletTx(pwallet, vTxHash, vWtx); - if (err != DB_LOAD_OK) - return err; - - // erase each wallet TX - BOOST_FOREACH (uint256& hash, vTxHash) { - if (!EraseTx(hash)) - return DB_CORRUPT; - } - - return DB_LOAD_OK; -} - -void ThreadFlushWalletDB(const string& strFile) -{ - // Make this thread recognisable as the wallet flushing thread - RenameThread("bitcoin-wallet"); - - static bool fOneThread; - if (fOneThread) - return; - fOneThread = true; - if (!GetBoolArg("-flushwallet", true)) - return; - - unsigned int nLastSeen = nWalletDBUpdated; - unsigned int nLastFlushed = nWalletDBUpdated; - int64_t nLastWalletUpdate = GetTime(); - while (true) - { - MilliSleep(500); - - if (nLastSeen != nWalletDBUpdated) - { - nLastSeen = nWalletDBUpdated; - nLastWalletUpdate = GetTime(); - } - - if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2) - { - TRY_LOCK(bitdb.cs_db,lockDb); - if (lockDb) - { - // Don't do this if any databases are in use - int nRefCount = 0; - map<string, int>::iterator mi = bitdb.mapFileUseCount.begin(); - while (mi != bitdb.mapFileUseCount.end()) - { - nRefCount += (*mi).second; - mi++; - } - - if (nRefCount == 0) - { - boost::this_thread::interruption_point(); - map<string, int>::iterator mi = bitdb.mapFileUseCount.find(strFile); - if (mi != bitdb.mapFileUseCount.end()) - { - LogPrint("db", "Flushing wallet.dat\n"); - nLastFlushed = nWalletDBUpdated; - int64_t nStart = GetTimeMillis(); - - // Flush wallet.dat so it's self contained - bitdb.CloseDb(strFile); - bitdb.CheckpointLSN(strFile); - - bitdb.mapFileUseCount.erase(mi++); - LogPrint("db", "Flushed wallet.dat %dms\n", GetTimeMillis() - nStart); - } - } - } - } - } -} - -bool BackupWallet(const CWallet& wallet, const string& strDest) -{ - if (!wallet.fFileBacked) - return false; - while (true) - { - { - LOCK(bitdb.cs_db); - if (!bitdb.mapFileUseCount.count(wallet.strWalletFile) || bitdb.mapFileUseCount[wallet.strWalletFile] == 0) - { - // Flush log data to the dat file - bitdb.CloseDb(wallet.strWalletFile); - bitdb.CheckpointLSN(wallet.strWalletFile); - bitdb.mapFileUseCount.erase(wallet.strWalletFile); - - // Copy wallet.dat - boost::filesystem::path pathSrc = GetDataDir() / wallet.strWalletFile; - boost::filesystem::path pathDest(strDest); - if (boost::filesystem::is_directory(pathDest)) - pathDest /= wallet.strWalletFile; - - try { -#if BOOST_VERSION >= 104000 - boost::filesystem::copy_file(pathSrc, pathDest, boost::filesystem::copy_option::overwrite_if_exists); -#else - boost::filesystem::copy_file(pathSrc, pathDest); -#endif - LogPrintf("copied wallet.dat to %s\n", pathDest.string()); - return true; - } catch (const boost::filesystem::filesystem_error& e) { - LogPrintf("error copying wallet.dat to %s - %s\n", pathDest.string(), e.what()); - return false; - } - } - } - MilliSleep(100); - } - return false; -} - -// -// Try to (very carefully!) recover wallet.dat if there is a problem. -// -bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys) -{ - // Recovery procedure: - // move wallet.dat to wallet.timestamp.bak - // Call Salvage with fAggressive=true to - // get as much data as possible. - // Rewrite salvaged data to wallet.dat - // Set -rescan so any missing transactions will be - // found. - int64_t now = GetTime(); - std::string newFilename = strprintf("wallet.%d.bak", now); - - int result = dbenv.dbenv->dbrename(NULL, filename.c_str(), NULL, - newFilename.c_str(), DB_AUTO_COMMIT); - if (result == 0) - LogPrintf("Renamed %s to %s\n", filename, newFilename); - else - { - LogPrintf("Failed to rename %s to %s\n", filename, newFilename); - return false; - } - - std::vector<CDBEnv::KeyValPair> salvagedData; - bool allOK = dbenv.Salvage(newFilename, true, salvagedData); - if (salvagedData.empty()) - { - LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename); - return false; - } - LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size()); - - bool fSuccess = allOK; - boost::scoped_ptr<Db> pdbCopy(new Db(dbenv.dbenv, 0)); - int ret = pdbCopy->open(NULL, // Txn pointer - filename.c_str(), // Filename - "main", // Logical db name - DB_BTREE, // Database type - DB_CREATE, // Flags - 0); - if (ret > 0) - { - LogPrintf("Cannot create database file %s\n", filename); - return false; - } - CWallet dummyWallet; - CWalletScanState wss; - - DbTxn* ptxn = dbenv.TxnBegin(); - BOOST_FOREACH(CDBEnv::KeyValPair& row, salvagedData) - { - if (fOnlyKeys) - { - CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION); - CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION); - string strType, strErr; - bool fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, - wss, strType, strErr); - if (!IsKeyType(strType)) - continue; - if (!fReadOK) - { - LogPrintf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType, strErr); - continue; - } - } - Dbt datKey(&row.first[0], row.first.size()); - Dbt datValue(&row.second[0], row.second.size()); - int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE); - if (ret2 > 0) - fSuccess = false; - } - ptxn->commit(0); - pdbCopy->close(0); - - return fSuccess; -} - -bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename) -{ - return CWalletDB::Recover(dbenv, filename, false); -} - -bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value) -{ - nWalletDBUpdated++; - return Write(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value); -} - -bool CWalletDB::EraseDestData(const std::string &address, const std::string &key) -{ - nWalletDBUpdated++; - return Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key))); -} |