Browse Source

Merge pull request #3674

77cbd46 Let -zapwallettxes recover transaction meta data (Cozz Lovan)
0.10
Wladimir J. van der Laan 11 years ago
parent
commit
d4392c8989
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6
  1. 8
      qa/rpc-tests/util.sh
  2. 161
      qa/rpc-tests/zapwallettxes.sh
  3. 34
      src/init.cpp
  4. 4
      src/wallet.cpp
  5. 2
      src/wallet.h
  6. 10
      src/walletdb.cpp
  7. 4
      src/walletdb.h

8
qa/rpc-tests/util.sh

@ -38,6 +38,10 @@ function AssertEqual {
if (( $( echo "$1 == $2" | bc ) == 0 )) if (( $( echo "$1 == $2" | bc ) == 0 ))
then then
echoerr "AssertEqual: $1 != $2" echoerr "AssertEqual: $1 != $2"
declare -f CleanUp > /dev/null 2>&1
if [[ $? -eq 0 ]] ; then
CleanUp
fi
exit 1 exit 1
fi fi
} }
@ -49,6 +53,10 @@ function CheckBalance {
if (( $( echo "$B == $EXPECT" | bc ) == 0 )) if (( $( echo "$B == $EXPECT" | bc ) == 0 ))
then then
echoerr "bad balance: $B (expected $2)" echoerr "bad balance: $B (expected $2)"
declare -f CleanUp > /dev/null 2>&1
if [[ $? -eq 0 ]] ; then
CleanUp
fi
exit 1 exit 1
fi fi
} }

161
qa/rpc-tests/zapwallettxes.sh

@ -0,0 +1,161 @@
#!/usr/bin/env bash
# Test -zapwallettxes=<mode>
if [ $# -lt 1 ]; then
echo "Usage: $0 path_to_binaries"
echo "e.g. $0 ../../src"
exit 1
fi
set -f
BITCOIND=${1}/bitcoind
CLI=${1}/bitcoin-cli
DIR="${BASH_SOURCE%/*}"
SENDANDWAIT="${DIR}/send.sh"
if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi
. "$DIR/util.sh"
D=$(mktemp -d test.XXXXX)
D1=${D}/node1
CreateDataDir "$D1" port=11000 rpcport=11001
B1ARGS="-datadir=$D1"
$BITCOIND $B1ARGS &
B1PID=$!
D2=${D}/node2
CreateDataDir "$D2" port=11010 rpcport=11011
B2ARGS="-datadir=$D2"
$BITCOIND $B2ARGS &
B2PID=$!
function CleanUp {
$CLI $B2ARGS stop > /dev/null 2>&1
wait $B2PID
$CLI $B1ARGS stop > /dev/null 2>&1
wait $B1PID
rm -rf $D
}
# 110 blocks, 10 mature == 500 XBT
$CLI $B1ARGS setgenerate true 110
$CLI $B2ARGS setgenerate true 110
CheckBalance "$B1ARGS" 500
CheckBalance "$B2ARGS" 500
# Send 10 XBT
TXID1_DEFAULT=$($CLI $B1ARGS sendtoaddress "mrhz5ZgSF3C1BSdyCKt3gEdhKoRL5BNfJV" 10)
TXID2_DEFAULT=$($CLI $B2ARGS sendtoaddress "mrhz5ZgSF3C1BSdyCKt3gEdhKoRL5BNfJV" 10)
CheckBalance $B1ARGS 490
CheckBalance $B2ARGS 490
# Move 10 XBT to testaccount
TMP=$($CLI $B1ARGS move "" "testaccount" 10)
TMP=$($CLI $B2ARGS move "" "testaccount" 10)
CheckBalance $B1ARGS 10 "testaccount"
CheckBalance $B2ARGS 10 "testaccount"
# Send 1 XBT from testaccount
TXID1_TESTACCOUNT=$($CLI $B1ARGS sendfrom "testaccount" "mrhz5ZgSF3C1BSdyCKt3gEdhKoRL5BNfJV" 1)
TXID2_TESTACCOUNT=$($CLI $B2ARGS sendfrom "testaccount" "mrhz5ZgSF3C1BSdyCKt3gEdhKoRL5BNfJV" 1)
CheckBalance $B1ARGS 9 "testaccount"
CheckBalance $B2ARGS 9 "testaccount"
CheckBalance $B1ARGS 489
CheckBalance $B2ARGS 489
# Confirm transactions
$CLI $B1ARGS setgenerate true 1
$CLI $B2ARGS setgenerate true 1
# Create unconfirmed transaction
TXID1_UNCONFIRMED=$($CLI $B1ARGS sendtoaddress "mrhz5ZgSF3C1BSdyCKt3gEdhKoRL5BNfJV" 1)
TXID2_UNCONFIRMED=$($CLI $B2ARGS sendtoaddress "mrhz5ZgSF3C1BSdyCKt3gEdhKoRL5BNfJV" 1)
# check balance (we created another 50 and spent 1 in the meantime)
CheckBalance $B1ARGS 538
CheckBalance $B2ARGS 538
# Safety check, if unconfirmed transactions are there
$CLI $B1ARGS gettransaction $TXID1_UNCONFIRMED > /dev/null 2>&1
if [[ $? -ne 0 ]] ; then
echoerr "gettransaction1_1: $TXID1_UNCONFIRMED failed"
CleanUp
exit 1
fi
$CLI $B2ARGS gettransaction $TXID2_UNCONFIRMED > /dev/null 2>&1
if [[ $? -ne 0 ]] ; then
echoerr "gettransaction2_1: $TXID2_UNCONFIRMED failed"
CleanUp
exit 1
fi
# stop nodes
$CLI $B2ARGS stop > /dev/null 2>&1
wait $B2PID
$CLI $B1ARGS stop > /dev/null 2>&1
wait $B1PID
# restart nodes with -zapwallettxes
$BITCOIND -zapwallettxes=1 $B1ARGS &
B1PID=$!
$BITCOIND -zapwallettxes=2 $B2ARGS &
B2PID=$!
# check if confirmed transactions are there
$CLI $B1ARGS gettransaction $TXID1_DEFAULT > /dev/null 2>&1
if [[ $? -ne 0 ]] ; then
echoerr "check confirmed transaction 1: $TXID1_DEFAULT failed"
CleanUp
exit 1
fi
$CLI $B2ARGS gettransaction $TXID2_DEFAULT > /dev/null 2>&1
if [[ $? -ne 0 ]] ; then
echoerr "check confirmed transaction 2: $TXID2_DEFAULT failed"
CleanUp
exit 1
fi
$CLI $B1ARGS gettransaction $TXID1_TESTACCOUNT > /dev/null 2>&1
if [[ $? -ne 0 ]] ; then
echoerr "check confirmed transaction 3: $TXID1_TESTACCOUNT failed"
CleanUp
exit 1
fi
$CLI $B2ARGS gettransaction $TXID2_TESTACCOUNT > /dev/null 2>&1
if [[ $? -ne 0 ]] ; then
echoerr "check confirmed transaction 4: $TXID2_TESTACCOUNT failed"
CleanUp
exit 1
fi
# check if unconfirmed transaction is gone
$CLI $B1ARGS gettransaction $TXID1_UNCONFIRMED > /dev/null 2>&1
if [[ $? -eq 0 ]] ; then
echoerr "check unconfirmed transaction 1: $TXID1_UNCONFIRMED failed"
CleanUp
exit 1
fi
$CLI $B2ARGS gettransaction $TXID2_UNCONFIRMED > /dev/null 2>&1
if [[ $? -eq 0 ]] ; then
echoerr "check unconfirmed transaction 2: $TXID2_UNCONFIRMED failed"
CleanUp
exit 1
fi
# check zapwallet mode 1, testaccount balance must be 9 (keeping transaction metadata)
CheckBalance $B1ARGS 9 "testaccount"
# check zapwallet mode 2, testaccount balance must be 10 (dropping transaction metadata)
CheckBalance $B2ARGS 10 "testaccount"
echo "Tests successful, cleaning up"
CleanUp
exit 0

34
src/init.cpp

@ -260,7 +260,8 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += " -upgradewallet " + _("Upgrade wallet to latest format") + " " + _("on startup") + "\n"; strUsage += " -upgradewallet " + _("Upgrade wallet to latest format") + " " + _("on startup") + "\n";
strUsage += " -wallet=<file> " + _("Specify wallet file (within data directory)") + " " + _("(default: wallet.dat)") + "\n"; strUsage += " -wallet=<file> " + _("Specify wallet file (within data directory)") + " " + _("(default: wallet.dat)") + "\n";
strUsage += " -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n"; strUsage += " -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n";
strUsage += " -zapwallettxes " + _("Clear list of wallet transactions (diagnostic tool; implies -rescan)") + "\n"; strUsage += " -zapwallettxes=<mode> " + _("Delete all wallet transactions and only recover those part of the blockchain through -rescan on startup") + "\n";
strUsage += " " + _("(default: 1, 1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)") + "\n";
#endif #endif
strUsage += "\n" + _("Debugging/Testing options:") + "\n"; strUsage += "\n" + _("Debugging/Testing options:") + "\n";
@ -536,7 +537,7 @@ bool AppInit2(boost::thread_group& threadGroup)
// -zapwallettx implies a rescan // -zapwallettx implies a rescan
if (GetBoolArg("-zapwallettxes", false)) { if (GetBoolArg("-zapwallettxes", false)) {
if (SoftSetBoolArg("-rescan", true)) if (SoftSetBoolArg("-rescan", true))
LogPrintf("AppInit2 : parameter interaction: -zapwallettxes=1 -> setting -rescan=1\n"); LogPrintf("AppInit2 : parameter interaction: -zapwallettxes=<mode> -> setting -rescan=1\n");
} }
// Make sure enough file descriptors are available // Make sure enough file descriptors are available
@ -993,11 +994,15 @@ bool AppInit2(boost::thread_group& threadGroup)
pwalletMain = NULL; pwalletMain = NULL;
LogPrintf("Wallet disabled!\n"); LogPrintf("Wallet disabled!\n");
} else { } else {
// needed to restore wallet transaction meta data after -zapwallettxes
std::vector<CWalletTx> vWtx;
if (GetBoolArg("-zapwallettxes", false)) { if (GetBoolArg("-zapwallettxes", false)) {
uiInterface.InitMessage(_("Zapping all transactions from wallet...")); uiInterface.InitMessage(_("Zapping all transactions from wallet..."));
pwalletMain = new CWallet(strWalletFile); pwalletMain = new CWallet(strWalletFile);
DBErrors nZapWalletRet = pwalletMain->ZapWalletTx(); DBErrors nZapWalletRet = pwalletMain->ZapWalletTx(vWtx);
if (nZapWalletRet != DB_LOAD_OK) { if (nZapWalletRet != DB_LOAD_OK) {
uiInterface.InitMessage(_("Error loading wallet.dat: Wallet corrupted")); uiInterface.InitMessage(_("Error loading wallet.dat: Wallet corrupted"));
return false; return false;
@ -1092,6 +1097,29 @@ bool AppInit2(boost::thread_group& threadGroup)
LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart); LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart);
pwalletMain->SetBestChain(chainActive.GetLocator()); pwalletMain->SetBestChain(chainActive.GetLocator());
nWalletDBUpdated++; 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) } // (!fDisableWallet)
#else // ENABLE_WALLET #else // ENABLE_WALLET

4
src/wallet.cpp

@ -1514,11 +1514,11 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
} }
DBErrors CWallet::ZapWalletTx() DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx)
{ {
if (!fFileBacked) if (!fFileBacked)
return DB_LOAD_OK; return DB_LOAD_OK;
DBErrors nZapWalletTxRet = CWalletDB(strWalletFile,"cr+").ZapWalletTx(this); DBErrors nZapWalletTxRet = CWalletDB(strWalletFile,"cr+").ZapWalletTx(this, vWtx);
if (nZapWalletTxRet == DB_NEED_REWRITE) if (nZapWalletTxRet == DB_NEED_REWRITE)
{ {
if (CDB::Rewrite(strWalletFile, "\x04pool")) if (CDB::Rewrite(strWalletFile, "\x04pool"))

2
src/wallet.h

@ -341,7 +341,7 @@ public:
void SetBestChain(const CBlockLocator& loc); void SetBestChain(const CBlockLocator& loc);
DBErrors LoadWallet(bool& fFirstRunRet); DBErrors LoadWallet(bool& fFirstRunRet);
DBErrors ZapWalletTx(); DBErrors ZapWalletTx(std::vector<CWalletTx>& vWtx);
bool SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& purpose); bool SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& purpose);

10
src/walletdb.cpp

@ -680,7 +680,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
return result; return result;
} }
DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& vTxHash) DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& vTxHash, vector<CWalletTx>& vWtx)
{ {
pwallet->vchDefaultKey = CPubKey(); pwallet->vchDefaultKey = CPubKey();
CWalletScanState wss; CWalletScanState wss;
@ -725,7 +725,11 @@ DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& vTxHash)
uint256 hash; uint256 hash;
ssKey >> hash; ssKey >> hash;
CWalletTx wtx;
ssValue >> wtx;
vTxHash.push_back(hash); vTxHash.push_back(hash);
vWtx.push_back(wtx);
} }
} }
pcursor->close(); pcursor->close();
@ -743,11 +747,11 @@ DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& vTxHash)
return result; return result;
} }
DBErrors CWalletDB::ZapWalletTx(CWallet* pwallet) DBErrors CWalletDB::ZapWalletTx(CWallet* pwallet, vector<CWalletTx>& vWtx)
{ {
// build list of wallet TXs // build list of wallet TXs
vector<uint256> vTxHash; vector<uint256> vTxHash;
DBErrors err = FindWalletTx(pwallet, vTxHash); DBErrors err = FindWalletTx(pwallet, vTxHash, vWtx);
if (err != DB_LOAD_OK) if (err != DB_LOAD_OK)
return err; return err;

4
src/walletdb.h

@ -122,8 +122,8 @@ public:
DBErrors ReorderTransactions(CWallet*); DBErrors ReorderTransactions(CWallet*);
DBErrors LoadWallet(CWallet* pwallet); DBErrors LoadWallet(CWallet* pwallet);
DBErrors FindWalletTx(CWallet* pwallet, std::vector<uint256>& vTxHash); DBErrors FindWalletTx(CWallet* pwallet, std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx);
DBErrors ZapWalletTx(CWallet* pwallet); 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, bool fOnlyKeys);
static bool Recover(CDBEnv& dbenv, std::string filename); static bool Recover(CDBEnv& dbenv, std::string filename);
}; };

Loading…
Cancel
Save