Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

backport: Merge bitcoin#23123 #6265

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
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
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 @@ -539,7 +539,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
Loading