Skip to content
Open
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
10 changes: 9 additions & 1 deletion doc/managing-wallets.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,15 @@ This means that a single backup is enough to recover the coins at any time. It i

Non-HD wallets must be backed up every 1000 keys used since the previous backup, or even more often to maintain the metadata.

### 1.6 Restoring the Wallet From a Backup
### 1.6 Automatic Backups

For legacy wallets, Dash Core automatically creates a backup in the `backups` directory inside the data directory on every startup and whenever the keypool is replenished. Each file is named after the wallet with the backup time appended, e.g. `wallet.dat.2026-08-02-14-30`.

Older backups are pruned as new ones are made. The most recent `-createwalletbackups` backups (default: 10, max: 20) are always kept. Beyond those, one backup is kept from each widening age range — 1-2 days old, 2-4 days, 4-8 days, and so on — up to `-maxwalletbackups` files in total (default: 30). This preserves restore points going back months while keeping the size of the `backups` directory predictable. Because the ranges are measured in days, several backups made within the same day do not each get their own range: only the most recent `-createwalletbackups` of them are kept.

Setting either option to `0` disables automatic backups, in which case existing backups are left untouched. Renaming a backup also excludes it from pruning, which is a simple way to keep one indefinitely.

### 1.7 Restoring the Wallet From a Backup

To restore a wallet, the `restorewallet` RPC or the `Restore Wallet` GUI menu item (`File` -> `Restore Wallet…`) must be used.

Expand Down
1 change: 1 addition & 0 deletions src/Makefile.test.include
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ BITCOIN_TESTS =\

if ENABLE_WALLET
BITCOIN_TESTS += \
wallet/test/backup_tests.cpp \
wallet/test/bip39_tests.cpp \
wallet/test/coinjoin_tests.cpp \
wallet/test/psbt_wallet_tests.cpp \
Expand Down
2 changes: 1 addition & 1 deletion src/coinjoin/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -843,7 +843,7 @@ bool CCoinJoinClientManager::CheckAutomaticBackup()
// We don't need auto-backups for descriptor wallets
if (!m_wallet->IsLegacy()) return true;

switch (nWalletBackups) {
switch (CWallet::nWalletBackups) {
case 0:
WalletCJLogPrint(m_wallet, "CCoinJoinClientManager::CheckAutomaticBackup -- Automatic backups disabled, no mixing available.\n");
stopMixing();
Expand Down
4 changes: 4 additions & 0 deletions src/interfaces/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ class Wallet
//! Get the number of keys since the last auto backup
virtual int64_t getKeysLeftSinceAutoBackup() = 0;

//! Get automatic backup status: >0 = enabled, 0 = disabled, -1 = failed,
//! -2 = wallet locked. Process-wide, not per-wallet.
virtual int getWalletBackupStatus() = 0;

//! Get wallet name.
virtual std::string getWalletName() = 0;

Expand Down
20 changes: 7 additions & 13 deletions src/qt/overviewpage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -524,15 +524,16 @@ void OverviewPage::coinJoinStatus(bool fForce)
if (fForce) nCachedNumBlocks = std::numeric_limits<int>::max();

// Disable any PS UI for masternode or when autobackup is disabled or failed for whatever reason
if (clientModel->node().isMasternode() || nWalletBackups <= 0) {
const int backup_status{walletModel->wallet().getWalletBackupStatus()};
if (clientModel->node().isMasternode() || backup_status <= 0) {
DisableCoinJoinCompletely();
if (nWalletBackups == 0) {
if (backup_status == 0) {
ui->labelCoinJoinEnabled->setToolTip(tr("Automatic backups are disabled, no mixing available!"));
} else if (nWalletBackups == -1) {
} else if (backup_status == -1) {
ui->labelCoinJoinEnabled->setToolTip(tr("ERROR! Failed to create automatic backup") + ", " +
tr("see debug.log for details.") + "<br><br>" +
tr("Mixing is disabled, please close your wallet and fix the issue!"));
} else if (nWalletBackups == -2) {
} else if (backup_status == -2) {
ui->labelCoinJoinEnabled->setToolTip(tr("WARNING! Failed to replenish keypool, please unlock your wallet to do so."));
}
return;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Expand Down Expand Up @@ -628,7 +629,7 @@ void OverviewPage::coinJoinStatus(bool fForce)

// Warn user that wallet is running out of keys
// NOTE: we do NOT warn user and do NOT create autobackups if mixing is not running
if (walletModel->wallet().isLegacy() && nWalletBackups > 0 && walletModel->getKeysLeftSinceAutoBackup() < COINJOIN_KEYS_THRESHOLD_WARNING) {
if (walletModel->wallet().isLegacy() && walletModel->wallet().getWalletBackupStatus() > 0 && walletModel->getKeysLeftSinceAutoBackup() < COINJOIN_KEYS_THRESHOLD_WARNING) {
QSettings settings;
if(settings.value("fLowKeysWarning").toBool()) {
QString strWarn = tr("Very low number of keys left since last automatic backup!") + "<br><br>" +
Expand Down Expand Up @@ -672,13 +673,6 @@ void OverviewPage::coinJoinStatus(bool fForce)
if(fShowAdvancedCJUI && !strKeysLeftText.isEmpty()) strEnabled += ", " + strKeysLeftText;
ui->labelCoinJoinEnabled->setText(strEnabled);

if (walletModel->wallet().isLegacy() && nWalletBackups == -1) {
// Automatic backup failed, nothing else we can do until user fixes the issue manually.
// Stop mixing right away; the guard above sets the matching tooltip on the next timer tick.
DisableCoinJoinCompletely();
return;
}

// check coinjoin status and unlock if needed
if (nBestHeight != nCachedNumBlocks) {
// Balance and number of transactions might have changed
Expand Down Expand Up @@ -781,7 +775,7 @@ void OverviewPage::DisableCoinJoinCompletely()

ui->toggleCoinJoin->setText("(" + tr("Disabled") + ")");
ui->frameCoinJoin->setEnabled(false);
if (nWalletBackups <= 0) {
if (walletModel && walletModel->wallet().getWalletBackupStatus() <= 0) {
ui->labelCoinJoinEnabled->setText("<span style='" + GUIUtil::getThemedStyleQString(GUIUtil::ThemedStyle::TS_ERROR) + "'>(" + tr("Disabled") + ")</span>");
}
walletModel->withCoinJoin([](auto& client) { client.stopMixing(); });
Expand Down
10 changes: 5 additions & 5 deletions src/qt/test/wallettests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,24 +198,24 @@ void TestGUI(interfaces::Node& node)
{
QLabel* coinJoinLabel = overviewPage.findChild<QLabel*>("labelCoinJoinEnabled");
QVERIFY(coinJoinLabel != nullptr);
const int nWalletBackupsOld = nWalletBackups;
const int nWalletBackupsOld = CWallet::nWalletBackups;

nWalletBackups = 0;
CWallet::nWalletBackups = 0;
overviewPage.coinJoinStatus(/*fForce=*/true);
QCOMPARE(coinJoinLabel->toolTip(), QString("Automatic backups are disabled, no mixing available!"));

nWalletBackups = -1;
CWallet::nWalletBackups = -1;
overviewPage.coinJoinStatus(/*fForce=*/true);
QCOMPARE(coinJoinLabel->toolTip(),
QString("ERROR! Failed to create automatic backup, see debug.log for details.<br><br>Mixing is "
"disabled, please close your wallet and fix the issue!"));

nWalletBackups = -2;
CWallet::nWalletBackups = -2;
overviewPage.coinJoinStatus(/*fForce=*/true);
QCOMPARE(coinJoinLabel->toolTip(),
QString("WARNING! Failed to replenish keypool, please unlock your wallet to do so."));

nWalletBackups = nWalletBackupsOld;
CWallet::nWalletBackups = nWalletBackupsOld;
}

// Check Request Payment button
Expand Down
9 changes: 0 additions & 9 deletions src/util/system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,6 @@ const int64_t nStartupTime = GetTime();
//Dash only features
const std::string gCoinJoinName = "CoinJoin";

/**
nWalletBackups:
1..10 - number of automatic backups to keep
0 - disabled by command-line
-1 - disabled because of some error during run-time
-2 - disabled because wallet was locked and we were not able to replenish keypool
*/
int nWalletBackups = 10;

const char * const BITCOIN_CONF_FILENAME = "dash.conf";
const char * const BITCOIN_SETTINGS_FILENAME = "settings.json";

Expand Down
1 change: 0 additions & 1 deletion src/util/system.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@

//Dash only features

extern int nWalletBackups;
extern const std::string gCoinJoinName;

class ArgsManager;
Expand Down
5 changes: 3 additions & 2 deletions src/wallet/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const
{
argsman.AddArg("-avoidpartialspends", strprintf("Group outputs by address, selecting many (possibly all) or none, instead of selecting on a per-output basis. Privacy is improved as addresses are mostly swept with fewer transactions and outputs are aggregated in clean change addresses. It may result in higher fees due to less optimal coin selection caused by this added limitation and possibly a larger-than-necessary number of inputs being used. Always enabled for wallets with \"avoid_reuse\" enabled, otherwise default: %u.", DEFAULT_AVOIDPARTIALSPENDS), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-consolidatefeerate=<amt>", strprintf("The maximum feerate (in %s/kvB) at which transaction building may use more inputs than strictly necessary so that the wallet's UTXO pool can be reduced (default: %s).", CURRENCY_UNIT, FormatMoney(DEFAULT_CONSOLIDATE_FEERATE)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-createwalletbackups=<n>", strprintf("Number of automatic wallet backups (default: %u)", nWalletBackups), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-createwalletbackups=<n>", strprintf("Number of most recent automatic wallet backups to keep, 0 to disable (default: %u, max: %u). Older backups are additionally kept at exponentially spaced intervals, see doc/managing-wallets.md.", DEFAULT_N_WALLET_BACKUPS, MAX_N_WALLET_BACKUPS), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-maxwalletbackups=<n>", strprintf("Maximum total number of automatic wallet backups to keep, 0 to disable (default: %u)", DEFAULT_MAX_BACKUPS), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-disablewallet", "Do not load the wallet and disable wallet RPC calls", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
#if HAVE_SYSTEM
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);
Expand Down Expand Up @@ -214,7 +215,7 @@ void WalletInit::InitCoinJoinSettings(CCoinJoinClientManager& mgr) const

void WalletInit::InitAutoBackup() const
{
CWallet::InitAutoBackup();
CWallet::InitAutoBackup(gArgs);
}
} // namespace wallet

Expand Down
1 change: 1 addition & 0 deletions src/wallet/interfaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ class WalletImpl : public Wallet
return m_wallet->AutoBackupWallet(wallet_path, error_string, warnings);
}
int64_t getKeysLeftSinceAutoBackup() override { return m_wallet->nKeysLeftSinceAutoBackup; }
int getWalletBackupStatus() override { return CWallet::nWalletBackups; }
std::string getWalletName() override { return m_wallet->GetName(); }
util::Result<CTxDestination> getNewDestination(const std::string& label) override
{
Expand Down
Loading