diff --git a/doc/release-notes.md b/doc/release-notes.md --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -5,3 +5,6 @@ This release includes the following features and fixes: + - The `-zapwallettxes` startup option has been removed and its functionality removed + from the wallet. This functionality has been superseded with the abandon transaction + feature. diff --git a/src/dummywallet.cpp b/src/dummywallet.cpp --- a/src/dummywallet.cpp +++ b/src/dummywallet.cpp @@ -31,7 +31,7 @@ "-keypool=", "-maxapsfee=", "-maxtxfee=", "-mintxfee=", "-paytxfee=", "-rescan", "-salvagewallet", "-spendzeroconfchange", "-upgradewallet", "-wallet=", "-walletbroadcast", - "-walletdir=", "-walletnotify=", "-zapwallettxes=", + "-walletdir=", "-walletnotify=", // Wallet debug options "-dblogsize=", "-flushwallet", "-privdb", "-walletrejectlongchains"}; argsman.AddHiddenArgs(opts); diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -135,13 +135,6 @@ "shell escaping used to invoke the command.", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); #endif - argsman.AddArg( - "-zapwallettxes=", - "Delete all wallet transactions and only recover those parts of the " - "blockchain through -rescan on startup (1 = keep tx meta data e.g. " - "payment request information, 2 = drop tx meta data)", - ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); - argsman.AddArg( "-dblogsize=", strprintf("Flush wallet database activity from memory to disk " @@ -167,6 +160,8 @@ DEFAULT_WALLET_REJECT_LONG_CHAINS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST); + + argsman.AddHiddenArgs({"-zapwallettxes"}); } bool WalletInit::ParameterInteraction() const { @@ -180,8 +175,6 @@ return true; } - const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1; - if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && gArgs.SoftSetBoolArg("-walletbroadcast", false)) { LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting " @@ -189,26 +182,11 @@ __func__); } - bool zapwallettxes = gArgs.GetBoolArg("-zapwallettxes", false); - // -zapwallettxes implies dropping the mempool on startup - if (zapwallettxes && gArgs.SoftSetBoolArg("-persistmempool", false)) { - LogPrintf("%s: parameter interaction: -zapwallettxes enabled -> " - "setting -persistmempool=0\n", - __func__); - } - - // -zapwallettxes implies a rescan - if (zapwallettxes) { - if (is_multiwallet) { - return InitError(strprintf( - Untranslated("%s is only allowed with a single wallet file"), - "-zapwallettxes")); - } - if (gArgs.SoftSetBoolArg("-rescan", true)) { - LogPrintf("%s: parameter interaction: -zapwallettxes enabled -> " - "setting -rescan=1\n", - __func__); - } + if (gArgs.IsArgSet("-zapwallettxes")) { + return InitError( + Untranslated("-zapwallettxes has been removed. If you are " + "attempting to remove a stuck transaction from your " + "wallet, please use abandontransaction instead.")); } if (gArgs.GetBoolArg("-sysperms", false)) { diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3102,7 +3102,7 @@ "Loads a wallet from a wallet file or directory." "\nNote that all wallet command-line options used when starting " "bitcoind will be" - "\napplied to the new wallet (eg -zapwallettxes, rescan, etc).\n", + "\napplied to the new wallet (eg -rescan, etc).\n", { {"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet directory or .dat file."}, diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1288,7 +1288,6 @@ void chainStateFlushed(const CBlockLocator &loc) override; DBErrors LoadWallet(bool &fFirstRunRet); - DBErrors ZapWalletTx(std::list &vWtx); DBErrors ZapSelectTx(std::vector &txIdsIn, std::vector &txIdsOut) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3540,23 +3540,6 @@ return DBErrors::LOAD_OK; } -DBErrors CWallet::ZapWalletTx(std::list &vWtx) { - DBErrors nZapWalletTxRet = WalletBatch(*database, "cr+").ZapWalletTx(vWtx); - if (nZapWalletTxRet == DBErrors::NEED_REWRITE) { - if (database->Rewrite("\x04pool")) { - for (const auto &spk_man_pair : m_spk_managers) { - spk_man_pair.second->RewriteDB(); - } - } - } - - if (nZapWalletTxRet != DBErrors::LOAD_OK) { - return nZapWalletTxRet; - } - - return DBErrors::LOAD_OK; -} - bool CWallet::SetAddressBookWithDB(WalletBatch &batch, const CTxDestination &address, const std::string &strName, @@ -4196,23 +4179,6 @@ const std::string walletFile = WalletDataFilePath(location.GetPath()).string(); - // Needed to restore wallet transaction meta data after -zapwallettxes - std::list vWtx; - - if (gArgs.GetBoolArg("-zapwallettxes", false)) { - chain.initMessage( - _("Zapping all transactions from wallet...").translated); - - std::unique_ptr tempWallet = std::make_unique( - &chain, location, CreateWalletDatabase(location.GetPath())); - DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx); - if (nZapWalletRet != DBErrors::LOAD_OK) { - error = - strprintf(_("Error loading %s: Wallet corrupted"), walletFile); - return nullptr; - } - } - chain.initMessage(_("Loading wallet...").translated); int64_t nStart = GetTimeMillis(); @@ -4512,29 +4478,6 @@ } walletInstance->chainStateFlushed(chain.getTipLocator()); walletInstance->database->IncrementUpdateCounter(); - - // Restore wallet transaction metadata after -zapwallettxes=1 - if (gArgs.GetBoolArg("-zapwallettxes", false) && - gArgs.GetArg("-zapwallettxes", "1") != "2") { - WalletBatch batch(*walletInstance->database); - - for (const CWalletTx &wtxOld : vWtx) { - const TxId txid = wtxOld.GetId(); - std::map::iterator mi = - walletInstance->mapWallet.find(txid); - if (mi != walletInstance->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->nOrderPos = copyFrom->nOrderPos; - batch.WriteTx(*copyTo); - } - } - } } { diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -267,7 +267,6 @@ DBErrors LoadWallet(CWallet *pwallet); DBErrors FindWalletTx(std::vector &txIds, std::list &vWtx); - DBErrors ZapWalletTx(std::list &vWtx); DBErrors ZapSelectTx(std::vector &txIdsIn, std::vector &txIdsOut); /* Function to determine if a certain KV/key-type is a key (cryptographical diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -1032,24 +1032,6 @@ return DBErrors::LOAD_OK; } -DBErrors WalletBatch::ZapWalletTx(std::list &vWtx) { - // Build list of wallet TXs. - std::vector txIds; - DBErrors err = FindWalletTx(txIds, vWtx); - if (err != DBErrors::LOAD_OK) { - return err; - } - - // Erase each wallet TX. - for (const TxId &txid : txIds) { - if (!EraseTx(txid)) { - return DBErrors::CORRUPT; - } - } - - return DBErrors::LOAD_OK; -} - void MaybeCompactWalletDB() { static std::atomic fOneThread; if (fOneThread.exchange(true)) { diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py --- a/test/functional/wallet_basic.py +++ b/test/functional/wallet_basic.py @@ -490,8 +490,6 @@ maintenance = [ '-rescan', '-reindex', - '-zapwallettxes=1', - '-zapwallettxes=2', ] chainlimit = 6 for m in maintenance: diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py --- a/test/functional/wallet_multiwallet.py +++ b/test/functional/wallet_multiwallet.py @@ -157,26 +157,6 @@ self.nodes[0].assert_start_raises_init_error( ['-walletdir=' + not_a_dir], 'Error: Specified -walletdir "' + not_a_dir + '" is not a directory') - self.log.info("Do not allow -zapwallettxes with multiwallet") - self.nodes[0].assert_start_raises_init_error( - [ - '-zapwallettxes', - '-wallet=w1', - '-wallet=w2'], - "Error: -zapwallettxes is only allowed with a single wallet file") - self.nodes[0].assert_start_raises_init_error( - [ - '-zapwallettxes=1', - '-wallet=w1', - '-wallet=w2'], - "Error: -zapwallettxes is only allowed with a single wallet file") - self.nodes[0].assert_start_raises_init_error( - [ - '-zapwallettxes=2', - '-wallet=w1', - '-wallet=w2'], - "Error: -zapwallettxes is only allowed with a single wallet file") - # if wallets/ doesn't exist, datadir should be the default wallet dir wallet_dir2 = data_dir('walletdir') os.rename(wallet_dir(), wallet_dir2) diff --git a/test/functional/wallet_zapwallettxes.py b/test/functional/wallet_zapwallettxes.py deleted file mode 100755 --- a/test/functional/wallet_zapwallettxes.py +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. -"""Test the zapwallettxes functionality. - -- start two bitcoind nodes -- create two transactions on node 0 - one is confirmed and one is unconfirmed. -- restart node 0 and verify that both the confirmed and the unconfirmed - transactions are still available. -- restart node 0 with zapwallettxes and persistmempool, and verify that both - the confirmed and the unconfirmed transactions are still available. -- restart node 0 with just zapwallettxes and verify that the confirmed - transactions are still available, but that the unconfirmed transaction has - been zapped. -""" -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import ( - assert_equal, - assert_raises_rpc_error, -) - - -class ZapWalletTXesTest (BitcoinTestFramework): - def set_test_params(self): - self.setup_clean_chain = True - self.num_nodes = 2 - - def skip_test_if_missing_module(self): - self.skip_if_no_wallet() - - def run_test(self): - self.log.info("Mining blocks...") - self.nodes[0].generate(1) - self.sync_all() - self.nodes[1].generate(100) - self.sync_all() - - # This transaction will be confirmed - txid1 = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 10) - - self.nodes[0].generate(1) - self.sync_all() - - # This transaction will not be confirmed - txid2 = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 20) - - # Confirmed and unconfirmed transactions are now in the wallet. - assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1) - assert_equal(self.nodes[0].gettransaction(txid2)['txid'], txid2) - - # Restart node0. Both confirmed and unconfirmed transactions remain - # in the wallet. - self.restart_node(0) - - assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1) - assert_equal(self.nodes[0].gettransaction(txid2)['txid'], txid2) - - # Restart node0 with zapwallettxes and persistmempool. The unconfirmed - # transaction is zapped from the wallet, but is re-added when the - # mempool is reloaded. - self.restart_node(0, ["-persistmempool=1", "-zapwallettxes=2"]) - - self.wait_until(lambda: self.nodes[0].getmempoolinfo()['size'] == 1, - timeout=3) - # Flush mempool to wallet - self.nodes[0].syncwithvalidationinterfacequeue() - - assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1) - assert_equal(self.nodes[0].gettransaction(txid2)['txid'], txid2) - - # Restart node0 with zapwallettxes, but not persistmempool. - # The unconfirmed transaction is zapped and is no longer in the wallet. - self.restart_node(0, ["-zapwallettxes=2"]) - - # tx1 is still be available because it was confirmed - assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1) - - # This will raise an exception because the unconfirmed transaction has - # been zapped - assert_raises_rpc_error(-5, 'Invalid or non-wallet transaction id', - self.nodes[0].gettransaction, txid2) - - -if __name__ == '__main__': - ZapWalletTXesTest().main() diff --git a/test/lint/check-doc.py b/test/lint/check-doc.py --- a/test/lint/check-doc.py +++ b/test/lint/check-doc.py @@ -48,6 +48,8 @@ '-forcecompactdb', '-parkdeepreorg', '-automaticunparking', + # Removed arguments that now just print a helpful error message + '-zapwallettxes', # Remove after November 2020 upgrade '-axionactivationtime', '-replayprotectionactivationtime',