Skip to content

Commit

Permalink
Merge bitcoin#23123: Remove -rescan startup parameter
Browse files Browse the repository at this point in the history
dc3ec74 Add rescan removal release note (Samuel Dobson)
bccd1d9 Remove -rescan startup parameter (Samuel Dobson)
f963b0f Corrupt wallet tx shouldn't trigger rescan of all wallets (Samuel Dobson)
6c00649 Remove outdated dummy wallet -salvagewallet arg (Samuel Dobson)

Pull request description:

  Remove the `-rescan` startup parameter.

  Rescans can be run with the `rescanblockchain` RPC.

  Rescans are still done on wallet-load if needed due to corruption, for example.

ACKs for top commit:
  achow101:
    ACK dc3ec74
  laanwj:
    re-ACK dc3ec74

Tree-SHA512: 608360d0e7d73737fd3ef408b01b33d97a75eebccd70c6d1b47a32fecb99b9105b520b111b225beb10611c09aa840a2b6d2b6e6e54be5d0362829e757289de5c
  • Loading branch information
laanwj authored and vijaydasmp committed Sep 11, 2024
1 parent 1464e69 commit 53b83e8
Show file tree
Hide file tree
Showing 17 changed files with 60 additions and 48 deletions.
2 changes: 1 addition & 1 deletion contrib/debian/examples/dash.conf
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@
#coinstatsindex=1

# Enable pruning to reduce storage requirements by deleting old blocks.
# This mode is incompatible with -txindex, -coinstatsindex and -rescan.
# This mode is incompatible with -txindex and -coinstatsindex.
# 0 = default (no pruning).
# 1 = allows manual pruning via RPC.
# >=945 = target to stay under in MiB.
Expand Down
9 changes: 9 additions & 0 deletions doc/release-notes-remove-rescan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Notable changes
===============

Rescan startup parameter removed
--------------------------------

The `-rescan` startup parameter has been removed. Wallets which require
rescanning due to corruption will still be rescanned on startup.
Otherwise, please use the `rescanblockchain` RPC to trigger a rescan.
2 changes: 0 additions & 2 deletions src/dummywallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ void DummyWalletInit::AddWalletOptions(ArgsManager& argsman) const
"-keypool=<n>",
"-maxapsfee=<n>",
"-maxtxfee=<amt>",
"-rescan=<mode>",
"-salvagewallet",
"-spendzeroconfchange",
"-wallet=<path>",
"-walletbackupsdir=<dir>",
Expand Down
2 changes: 1 addition & 1 deletion src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ void SetupServerArgs(ArgsManager& argsman)
-GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-persistmempool", strprintf("Whether to save the mempool on shutdown and load on restart (default: %u)", DEFAULT_PERSIST_MEMPOOL), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-pid=<file>", strprintf("Specify pid file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)", BITCOIN_PID_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-prune=<n>", strprintf("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex, -coinstatsindex, -rescan and -disablegovernance=false. "
argsman.AddArg("-prune=<n>", strprintf("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex, and -disablegovernance=false. "
"Warning: Reverting this setting requires re-downloading the entire blockchain. "
"(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB)", MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-settings=<file>", strprintf("Specify path to dynamic settings data file. Can be disabled with -nosettings. File is written at runtime and not meant to be edited by users (use %s instead for custom settings). Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME, BITCOIN_SETTINGS_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
Expand Down
2 changes: 0 additions & 2 deletions src/wallet/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const
argsman.AddArg("-instantsendnotify=<cmd>", "Execute command when a wallet InstantSend transaction is successfully locked. %s in cmd is replaced by TxID and %w is replaced by wallet name. %w is not currently implemented on Windows. On systems where %w is supported, it should NOT be quoted because this would break shell escaping used to invoke the command.", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
#endif
argsman.AddArg("-keypool=<n>", strprintf("Set key pool size to <n> (default: %u). Warning: Smaller sizes may increase the risk of losing funds when restoring from an old backup, if none of the addresses in the original keypool have been used.", DEFAULT_KEYPOOL_SIZE), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-rescan=<mode>", "Rescan the block chain for missing wallet transactions on startup"
" (1 = start from wallet creation time, 2 = start from genesis block)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-spendzeroconfchange", strprintf("Spend unconfirmed change when sending transactions (default: %u)", DEFAULT_SPEND_ZEROCONF_CHANGE), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-wallet=<path>", "Specify wallet path to load at startup. Can be used multiple times to load multiple wallets. Path is to a directory containing wallet data and log files. If the path is not absolute, it is interpreted relative to <walletdir>. This only loads existing wallets and does not create new ones. For backwards compatibility this also accepts names of existing top-level data files in <walletdir>.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET);
argsman.AddArg("-walletbackupsdir=<dir>", "Specify full path to directory for automatic wallet backups (must exist)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
Expand Down
4 changes: 2 additions & 2 deletions src/wallet/rpcdump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1626,7 +1626,7 @@ RPCHelpMan importmulti()
"and coins using this key may not appear in the wallet. This error could be "
"caused by pruning or data corruption (see dashd log for details) and could "
"be dealt with by downloading and rescanning the relevant blocks (see -reindex "
"and -rescan options).",
"option and rescanblockchain RPC).",
GetImportTimestamp(request, now), scannedTime - TIMESTAMP_WINDOW - 1, TIMESTAMP_WINDOW)));
response.push_back(std::move(result));
}
Expand Down Expand Up @@ -1924,7 +1924,7 @@ RPCHelpMan importdescriptors() {
"and coins using this desc may not appear in the wallet. This error could be "
"caused by pruning or data corruption (see bitcoind log for details) and could "
"be dealt with by downloading and rescanning the relevant blocks (see -reindex "
"and -rescan options).",
"option and rescanblockchain RPC).",
GetImportTimestamp(request, now), scanned_time - TIMESTAMP_WINDOW - 1, TIMESTAMP_WINDOW)));
response.push_back(std::move(result));
}
Expand Down
2 changes: 1 addition & 1 deletion src/wallet/rpcwallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2856,7 +2856,7 @@ static RPCHelpMan loadwallet()
return RPCHelpMan{"loadwallet",
"\nLoads a wallet from a wallet file or directory."
"\nNote that all wallet command-line options used when starting dashd will be"
"\napplied to the new wallet (eg, rescan, etc).\n",
"\napplied to the new wallet .\n",
{
{"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet directory or .dat file."},
{"load_on_startup", RPCArg::Type::BOOL, /* default */ "null", "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
Expand Down
2 changes: 1 addition & 1 deletion src/wallet/salvage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ bool RecoverDatabaseFile(const fs::path& file_path, bilingual_str& error, std::v
// Call Salvage with fAggressive=true to
// get as much data as possible.
// Rewrite salvaged data to fresh wallet file
// Set -rescan so any missing transactions will be
// Rescan so any missing transactions will be
// found.
int64_t now = GetTime();
std::string newFilename = strprintf("%s.%d.bak", filename, now);
Expand Down
4 changes: 2 additions & 2 deletions src/wallet/test/wallet_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,8 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
"seconds of key creation, and could contain transactions pertaining to the key. As a result, "
"transactions and coins using this key may not appear in the wallet. This error could be caused "
"by pruning or data corruption (see dashd log for details) and could be dealt with by "
"downloading and rescanning the relevant blocks (see -reindex and -rescan "
"options).\"}},{\"success\":true}]",
"downloading and rescanning the relevant blocks (see -reindex "
"option).\"}},{\"success\":true}]",
0, oldTip->GetBlockTimeMax(), TIMESTAMP_WINDOW));
RemoveWallet(wallet, std::nullopt);
}
Expand Down
23 changes: 15 additions & 8 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2033,7 +2033,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc

WalletLogPrintf("Rescan started from block %s...\n", start_block.ToString());

ShowProgress(strprintf("%s " + _("Rescanning…").translated, GetDisplayName()), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
ShowProgress(strprintf("%s " + _("Rescanning…").translated, GetDisplayName()), 0); // show rescan progress in GUI as dialog or on splashscreen, if rescan required on startup (e.g. due to corruption)
uint256 tip_hash = WITH_LOCK(cs_wallet, return GetLastBlockHash());
uint256 end_hash = tip_hash;
if (max_height) chain().findAncestorByHeight(tip_hash, *max_height, FoundBlock().hash(end_hash));
Expand Down Expand Up @@ -4014,15 +4014,12 @@ DBErrors CWallet::LoadWallet()
}
}

if (nLoadWalletRet != DBErrors::LOAD_OK)
return nLoadWalletRet;

/* If the CoinJoin salt is not set, try to set a new random hash as the salt */
if (GetCoinJoinSalt().IsNull() && !SetCoinJoinSalt(GetRandHash())) {
return DBErrors::LOAD_FAIL;
}

return DBErrors::LOAD_OK;
return nLoadWalletRet;
}

// Goes through all wallet transactions and checks if they are masternode collaterals, in which case these are locked
Expand Down Expand Up @@ -4702,6 +4699,7 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain* chain, interfaces::C
// TODO: Can't use std::make_shared because we need a custom deleter but
// should be possible to use std::allocate_shared.
std::shared_ptr<CWallet> walletInstance(new CWallet(chain, coinjoin_loader, name, std::move(database)), ReleaseWallet);
bool rescan_required = false;
// TODO: refactor this condition: validation of error looks like workaround
if (!walletInstance->AutoBackupWallet(fs::PathFromString(walletFile), error, warnings) && !error.original.empty()) {
return nullptr;
Expand All @@ -4727,6 +4725,14 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain* chain, interfaces::C
{
error = strprintf(_("Wallet needed to be rewritten: restart %s to complete"), PACKAGE_NAME);
return nullptr;
} else if (nLoadWalletRet == DBErrors::RESCAN_REQUIRED) {
warnings.push_back(strprintf(_("Error reading %s! Transaction data may be missing or incorrect."
" Rescanning wallet."), walletFile));
rescan_required = true;
} else if (nLoadWalletRet == DBErrors::RESCAN_REQUIRED) {
warnings.push_back(strprintf(_("Error reading %s! Transaction data may be missing or incorrect."
" Rescanning wallet."), walletFile));
rescan_required = true;
}
else {
error = strprintf(_("Error loading %s"), walletFile);
Expand Down Expand Up @@ -4949,7 +4955,7 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain* chain, interfaces::C
// Try to top up keypool. No-op if the wallet is locked.
walletInstance->TopUpKeyPool();

if (chain && !AttachChain(walletInstance, *chain, error, warnings)) {
if (chain && !AttachChain(walletInstance, *chain, rescan_required, error, warnings)) {
return nullptr;
}

Expand Down Expand Up @@ -4980,7 +4986,7 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain* chain, interfaces::C
return walletInstance;
}

bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interfaces::Chain& chain, bilingual_str& error, std::vector<bilingual_str>& warnings)
bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interfaces::Chain& chain, const bool rescan_required, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
LOCK(walletInstance->cs_wallet);
// allow setting the chain if it hasn't been set already but prevent changing it
Expand All @@ -5000,8 +5006,9 @@ bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interf
walletInstance->m_attaching_chain = true; //ignores chainStateFlushed notifications
walletInstance->m_chain_notifications_handler = walletInstance->chain().handleNotifications(walletInstance);

// If rescan_required = true, rescan_height remains equal to 0
int rescan_height = 0;
if (!gArgs.GetBoolArg("-rescan", false))
if (!rescan_required)
{
WalletBatch batch(walletInstance->GetDatabase());
CBlockLocator locator;
Expand Down
2 changes: 1 addition & 1 deletion src/wallet/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -853,7 +853,7 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati
* block locator and m_last_block_processed, and registering for
* notifications about new blocks and transactions.
*/
static bool AttachChain(const std::shared_ptr<CWallet>& wallet, interfaces::Chain& chain, bilingual_str& error, std::vector<bilingual_str>& warnings);
static bool AttachChain(const std::shared_ptr<CWallet>& wallet, interfaces::Chain& chain, const bool rescan_required, bilingual_str& error, std::vector<bilingual_str>& warnings);

public:
/**
Expand Down
8 changes: 6 additions & 2 deletions src/wallet/walletdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,7 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
{
CWalletScanState wss;
bool fNoncriticalErrors = false;
bool rescan_required = false;
DBErrors result = DBErrors::LOAD_OK;

LOCK(pwallet->cs_wallet);
Expand Down Expand Up @@ -802,7 +803,7 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
fNoncriticalErrors = true; // ... but do warn the user there is something wrong.
if (strType == DBKeys::TX)
// Rescan if there is a bad transaction record:
gArgs.SoftSetBoolArg("-rescan", true);
rescan_required = true;
}
}
if (!strErr.empty())
Expand Down Expand Up @@ -842,8 +843,11 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
((DescriptorScriptPubKeyMan*)spk_man)->AddCryptedKey(desc_key_pair.first.second, desc_key_pair.second.first, desc_key_pair.second.second);
}

if (fNoncriticalErrors && result == DBErrors::LOAD_OK)
if (rescan_required && result == DBErrors::LOAD_OK) {
result = DBErrors::RESCAN_REQUIRED;
} else if (fNoncriticalErrors && result == DBErrors::LOAD_OK) {
result = DBErrors::NONCRITICAL_ERROR;
}

// Any wallet corruption at all: skip any rewriting or
// upgrading, we don't want to make it worse.
Expand Down
3 changes: 2 additions & 1 deletion src/wallet/walletdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ enum class DBErrors
NONCRITICAL_ERROR,
TOO_NEW,
LOAD_FAIL,
NEED_REWRITE
NEED_REWRITE,
RESCAN_REQUIRED
};

namespace DBKeys {
Expand Down
4 changes: 4 additions & 0 deletions src/wallet/wallettool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ static std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::pa
} else if (load_wallet_ret == DBErrors::NEED_REWRITE) {
tfm::format(std::cerr, "Wallet needed to be rewritten: restart %s to complete", PACKAGE_NAME);
return nullptr;
} else if (load_wallet_ret == DBErrors::RESCAN_REQUIRED) {
tfm::format(std::cerr, "Error reading %s! Some transaction data might be missing or"
" incorrect. Wallet requires a rescan.",
name);
} else {
tfm::format(std::cerr, "Error loading %s", name);
return nullptr;
Expand Down
11 changes: 4 additions & 7 deletions test/functional/feature_notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ def setup_network(self):
# -alertnotify and -blocknotify on node0, walletnotify on node1
self.extra_args[0].append("-alertnotify=echo > {}".format(os.path.join(self.alertnotify_dir, '%s')))
self.extra_args[0].append("-blocknotify=echo > {}".format(os.path.join(self.blocknotify_dir, '%s')))
self.extra_args[1].append("-rescan")
self.extra_args[1].append("-walletnotify=echo %h_%b > {}".format(os.path.join(self.walletnotify_dir, notify_outputname('%w', '%s'))))

# -chainlocknotify on node0, -instantsendnotify on node1
Expand Down Expand Up @@ -79,17 +78,15 @@ def run_test(self):

# directory content should equal the generated transaction hashes
tx_details = list(map(lambda t: (t['txid'], t['blockheight'], t['blockhash']), self.nodes[1].listtransactions("*", block_count)))
self.stop_node(1)
self.expect_wallet_notify(tx_details)

self.log.info("test -walletnotify after rescan")
# restart node to rescan to force wallet notifications
self.start_node(1)
force_finish_mnsync(self.nodes[1])
self.connect_nodes(0, 1)

# rescan to force wallet notifications
self.nodes[1].rescanblockchain()
self.wait_until(lambda: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10)

self.connect_nodes(0, 1)

# directory content should equal the generated transaction hashes
tx_details = list(map(lambda t: (t['txid'], t['blockheight'], t['blockhash']), self.nodes[1].listtransactions("*", block_count)))
self.expect_wallet_notify(tx_details)
Expand Down
26 changes: 10 additions & 16 deletions test/functional/wallet_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -540,23 +540,17 @@ def run_test(self):
assert label in self.nodes[0].listlabels()
self.nodes[0].rpc.ensure_ascii = True # restore to default

# maintenance tests
maintenance = [
'-rescan',
'-reindex',
]
# -reindex tests
chainlimit = 6
for m in maintenance:
self.log.info("check " + m)
self.stop_nodes()
# set lower ancestor limit for later
self.start_node(0, [m, "-limitancestorcount=" + str(chainlimit)])
self.start_node(1, [m, "-limitancestorcount=" + str(chainlimit)])
self.start_node(2, [m, "-limitancestorcount=" + str(chainlimit)])
if m == '-reindex':
# reindex will leave rpc warm up "early"; Wait for it to finish
self.wait_until(lambda: [block_count] * 3 == [self.nodes[i].getblockcount() for i in range(3)])
assert_equal(balance_nodes, [self.nodes[i].getbalance() for i in range(3)])
self.log.info("Test -reindex")
self.stop_nodes()
# set lower ancestor limit for later
self.start_node(0, ['-reindex', "-limitancestorcount=" + str(chainlimit)])
self.start_node(1, ['-reindex', "-limitancestorcount=" + str(chainlimit)])
self.start_node(2, ['-reindex', "-limitancestorcount=" + str(chainlimit)])
# reindex will leave rpc warm up "early"; Wait for it to finish
self.wait_until(lambda: [block_count] * 3 == [self.nodes[i].getblockcount() for i in range(3)])
assert_equal(balance_nodes, [self.nodes[i].getbalance() for i in range(3)])

# Exercise listsinceblock with the last two blocks
coinbase_tx_1 = self.nodes[0].listsinceblock(blocks[0])
Expand Down
2 changes: 1 addition & 1 deletion test/functional/wallet_hd.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def run_test(self):
self.sync_all()

# Needs rescan
self.restart_node(1, extra_args=self.extra_args[1] + ['-rescan'])
self.nodes[1].rescanblockchain()
assert_equal(self.nodes[1].getbalance(), NUM_HD_ADDS + 1)

# Try a RPC based rescan
Expand Down

0 comments on commit 53b83e8

Please sign in to comment.