From: MASM fan Date: Sun, 16 Feb 2014 01:09:28 +0000 (+0400) Subject: Add -zapwallettxes cli/config option, used for wallet recovery X-Git-Tag: nvc-v0.5.3~76 X-Git-Url: https://git.novaco.in/?p=novacoin.git;a=commitdiff_plain;h=30785656536625e26b09b6b6dbcb4c062ba10cc1 Add -zapwallettxes cli/config option, used for wallet recovery This diagnostic tool removes all "tx" records from the wallet db, then forces a full rescan, to rebuild "tx" records accurately. --- diff --git a/src/init.cpp b/src/init.cpp index 22d345f..1f7fade 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -305,6 +305,7 @@ std::string HelpMessage() " -upgradewallet " + _("Upgrade wallet to latest format") + "\n" + " -keypool= " + _("Set key pool size to (default: 100)") + "\n" + " -rescan " + _("Rescan the block chain for missing wallet transactions") + "\n" + + " -zapwallettxes " + _("Clear list of wallet transactions (diagnostic tool; implies -rescan)") + "\n" + " -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + "\n" + " -checkblocks= " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" + " -checklevel= " + _("How thorough the block verification is (0-6, default: 1)") + "\n" + @@ -437,6 +438,12 @@ bool AppInit2() SoftSetBoolArg("-rescan", true); } + if (GetBoolArg("-zapwallettxes", false)) { + // -zapwallettx implies a rescan + if (SoftSetBoolArg("-rescan", true)) + printf("AppInit2 : parameter interaction: -zapwallettxes=1 -> setting -rescan=1\n"); + } + // ********************************************************* Step 3: parameter-to-internal-flags // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency @@ -839,6 +846,19 @@ bool AppInit2() // ********************************************************* Step 8: load wallet + if (GetBoolArg("-zapwallettxes", false)) { + uiInterface.InitMessage(_("Zapping all transactions from wallet...")); + + pwalletMain = new CWallet(strWalletFileName); + DBErrors nZapWalletRet = pwalletMain->ZapWalletTx(); + if (nZapWalletRet != DB_LOAD_OK) { + uiInterface.InitMessage(_("Error loading wallet.dat: Wallet corrupted")); + return false; + } + delete pwalletMain; + pwalletMain = NULL; + } + uiInterface.InitMessage(_("Loading wallet...")); printf("Loading wallet...\n"); nStart = GetTimeMillis(); diff --git a/src/wallet.cpp b/src/wallet.cpp index 45823f6..b484fc2 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -2243,6 +2243,28 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) return DB_LOAD_OK; } +DBErrors CWallet::ZapWalletTx() +{ + if (!fFileBacked) + return DB_LOAD_OK; + DBErrors nZapWalletTxRet = CWalletDB(strWalletFile,"cr+").ZapWalletTx(this); + if (nZapWalletTxRet == DB_NEED_REWRITE) + { + if (CDB::Rewrite(strWalletFile, "\x04pool")) + { + LOCK(cs_wallet); + setKeyPool.clear(); + // Note: can't top-up keypool here, because wallet is locked. + // User will be prompted to unlock wallet the next operation + // the requires a new key. + } + } + + if (nZapWalletTxRet != DB_LOAD_OK) + return nZapWalletTxRet; + + return DB_LOAD_OK; +} bool CWallet::SetAddressBookName(const CTxDestination& address, const string& strName) { diff --git a/src/wallet.h b/src/wallet.h index 803c273..cb7c238 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -311,6 +311,8 @@ public: DBErrors LoadWallet(bool& fFirstRunRet); + DBErrors ZapWalletTx(); + bool SetAddressBookName(const CTxDestination& address, const std::string& strName); bool DelAddressBookName(const CTxDestination& address); diff --git a/src/walletdb.cpp b/src/walletdb.cpp index ebe2003..a7529d1 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -554,6 +554,86 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) return result; } +DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector& vTxHash) +{ + 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) + { + printf("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) + { + printf("Error reading next record from wallet database\n"); + return DB_CORRUPT; + } + + string strType; + ssKey >> strType; + if (strType == "tx") { + uint256 hash; + ssKey >> hash; + + vTxHash.push_back(hash); + } + } + pcursor->close(); + } + catch (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) +{ + // build list of wallet TXs + vector vTxHash; + DBErrors err = FindWalletTx(pwallet, vTxHash); + 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(void* parg) { // Make this thread recognisable as the wallet flushing thread diff --git a/src/walletdb.h b/src/walletdb.h index b5b7f02..b6d4670 100644 --- a/src/walletdb.h +++ b/src/walletdb.h @@ -204,6 +204,9 @@ public: DBErrors ReorderTransactions(CWallet*); DBErrors LoadWallet(CWallet* pwallet); + DBErrors FindWalletTx(CWallet* pwallet, std::vector& vTxHash); + DBErrors ZapWalletTx(CWallet* pwallet); + static bool Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys); static bool Recover(CDBEnv& dbenv, std::string filename); };