Skip to content

Commit

Permalink
Add -zapwallettxes cli/config option, used for wallet recovery
Browse files Browse the repository at this point in the history
This diagnostic tool removes all "tx" records from the wallet db,
then forces a full rescan, to rebuild "tx" records accurately.
  • Loading branch information
0xDEADFACE authored and CryptoManiac committed Mar 2, 2015
1 parent 07e9089 commit 3078565
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 0 deletions.
20 changes: 20 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ std::string HelpMessage()
" -upgradewallet " + _("Upgrade wallet to latest format") + "\n" +
" -keypool=<n> " + _("Set key pool size to <n> (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=<n> " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" +
" -checklevel=<n> " + _("How thorough the block verification is (0-6, default: 1)") + "\n" +
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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();
Expand Down
22 changes: 22 additions & 0 deletions src/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
2 changes: 2 additions & 0 deletions src/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,8 @@ class CWallet : public CCryptoKeyStore

DBErrors LoadWallet(bool& fFirstRunRet);

DBErrors ZapWalletTx();

bool SetAddressBookName(const CTxDestination& address, const std::string& strName);

bool DelAddressBookName(const CTxDestination& address);
Expand Down
80 changes: 80 additions & 0 deletions src/walletdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,86 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
return result;
}

DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& 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<uint256> 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
Expand Down
3 changes: 3 additions & 0 deletions src/walletdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,9 @@ class CWalletDB : public CDB

DBErrors ReorderTransactions(CWallet*);
DBErrors LoadWallet(CWallet* pwallet);
DBErrors FindWalletTx(CWallet* pwallet, std::vector<uint256>& vTxHash);
DBErrors ZapWalletTx(CWallet* pwallet);

static bool Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys);
static bool Recover(CDBEnv& dbenv, std::string filename);
};
Expand Down

0 comments on commit 3078565

Please sign in to comment.