Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,8 @@ std::string HelpMessage()
" -rpcthreads=<n> " + _("Set the number of threads to service RPC calls (default: 4)") + "\n" +
" -blocknotify=<cmd> " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n" +
" -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n" +
" -zapwallettxes=<mode> " + _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") + "\n" +
" " + _("(default: 1, 1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)") + "\n" +
" -alertnotify=<cmd> " + _("Execute command when a relevant alert is received (%s in cmd is replaced by message)") + "\n" +
" -upgradewallet " + _("Upgrade wallet to latest format") + "\n" +
" -keypool=<n> " + _("Set key pool size to <n> (default: 100)") + "\n" +
Expand Down Expand Up @@ -543,6 +545,12 @@ bool AppInit2(boost::thread_group& threadGroup)
SoftSetBoolArg("-rescan", true);
}

// -zapwallettx implies a rescan
if (GetBoolArg("-zapwallettxes", false)) {
if (SoftSetBoolArg("-rescan", true))
LogPrintf("AppInit2 : parameter interaction: -zapwallettxes=<mode> -> setting -rescan=1\n");
}

// Make sure enough file descriptors are available
int nBind = std::max((int)mapArgs.count("-bind"), 1);
nMaxConnections = GetArg("-maxconnections", 125);
Expand Down Expand Up @@ -1009,6 +1017,23 @@ bool AppInit2(boost::thread_group& threadGroup)
LogPrintf("Wallet disabled!\n");
pwalletMain = NULL;
} else {
// needed to restore wallet transaction meta data after -zapwallettxes
std::vector<CWalletTx> vWtx;

if (GetBoolArg("-zapwallettxes", false)) {
uiInterface.InitMessage(_("Zapping all transactions from wallet..."));

pwalletMain = new CWallet("wallet.dat");
DBErrors nZapWalletRet = pwalletMain->ZapWalletTx(vWtx);
if (nZapWalletRet != DB_LOAD_OK) {
uiInterface.InitMessage(_("Error loading wallet.dat: Wallet corrupted"));
return false;
}

delete pwalletMain;
pwalletMain = NULL;
}

uiInterface.InitMessage(_("Loading wallet..."));

nStart = GetTimeMillis();
Expand Down Expand Up @@ -1094,6 +1119,29 @@ bool AppInit2(boost::thread_group& threadGroup)
LogPrintf(" rescan %15"PRI64d"ms\n", GetTimeMillis() - nStart);
pwalletMain->SetBestChain(CBlockLocator(pindexBest));
nWalletDBUpdated++;

// Restore wallet transaction metadata after -zapwallettxes=1
if (GetBoolArg("-zapwallettxes", false) && GetArg("-zapwallettxes", "1") != "2")
{
BOOST_FOREACH(const CWalletTx& wtxOld, vWtx)
{
uint256 hash = wtxOld.GetHash();
std::map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
if (mi != pwalletMain->mapWallet.end())
{
const CWalletTx* copyFrom = &wtxOld;
CWalletTx* copyTo = &mi->second;
copyTo->mapValue = copyFrom->mapValue;
copyTo->vOrderForm = copyFrom->vOrderForm;
copyTo->nTimeReceived = copyFrom->nTimeReceived;
copyTo->nTimeSmart = copyFrom->nTimeSmart;
copyTo->fFromMe = copyFrom->fFromMe;
copyTo->strFromAccount = copyFrom->strFromAccount;
copyTo->nOrderPos = copyFrom->nOrderPos;
copyTo->WriteToDisk();
}
}
}
}
} // (!fDisableWallet)

Expand Down
22 changes: 22 additions & 0 deletions src/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1459,6 +1459,28 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
return DB_LOAD_OK;
}

DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx)
{
if (!fFileBacked)
return DB_LOAD_OK;
DBErrors nZapWalletTxRet = CWalletDB(strWalletFile,"cr+").ZapWalletTx(this, vWtx);
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
1 change: 1 addition & 0 deletions src/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ class CWallet : public CCryptoKeyStore
void SetBestChain(const CBlockLocator& loc);

DBErrors LoadWallet(bool& fFirstRunRet);
DBErrors ZapWalletTx(std::vector<CWalletTx>& vWtx);

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

Expand Down
101 changes: 101 additions & 0 deletions src/walletdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,23 @@ CWalletDB::ReorderTransactions(CWallet* pwallet)
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,
Expand Down Expand Up @@ -466,6 +483,90 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
return result;
}

DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& vTxHash, vector<CWalletTx>& vWtx)
{
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;
}

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 (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
Expand Down
2 changes: 2 additions & 0 deletions src/walletdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ class CWalletDB : public CDB

DBErrors ReorderTransactions(CWallet*);
DBErrors LoadWallet(CWallet* pwallet);
DBErrors FindWalletTx(CWallet* pwallet, std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx);
DBErrors ZapWalletTx(CWallet* pwallet, std::vector<CWalletTx>& vWtx);
static bool Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys);
static bool Recover(CDBEnv& dbenv, std::string filename);
};
Expand Down