Auto-backup wallet.dat before flush/rewrite operations

- Add AutoBackupWallet() that copies wallet.dat to wallet.dat.auto.bak
  before any DB flush or rewrite
- Call AutoBackupWallet() in ThreadFlushWalletDB() before flushing
- Call AutoBackupWallet() in AppInit2() after loading wallet
- Add suspicious-size check in AppInit2() (warns if wallet.dat < 1KB)
- Declare AutoBackupWallet() in db.h

This protects against wallet corruption during crash by maintaining
an auto-backup that is always at least as recent as the last flush.
This commit is contained in:
Krystie
2026-04-18 23:34:19 -07:00
parent caddfb1789
commit 1881ff867e
3 changed files with 54 additions and 0 deletions
+1
View File
@@ -28,6 +28,7 @@ extern unsigned int nWalletDBUpdated;
void ThreadFlushWalletDB(void* parg);
bool BackupWallet(const CWallet& wallet, const std::string& strDest);
bool AutoBackupWallet(const boost::filesystem::path& walletPath);
class CDBEnv
+15
View File
@@ -1041,6 +1041,21 @@ bool AppInit2()
nStart = GetTimeMillis();
bool fFirstRun = true;
pwalletMain = new CWallet(strWalletFileName);
// Auto-backup wallet.dat before loading (protects against corruption during load/flush)
{
fs::path walletPath = GetDataDir() / strWalletFileName;
if (fs::exists(walletPath)) {
uintmax_t wsize = fs::file_size(walletPath);
printf("Wallet file size: %llu bytes\n", (unsigned long long)wsize);
if (wsize < 1024) {
strErrors << _("WARNING: wallet.dat is suspiciously small (") << wsize << _(" bytes). It may be corrupt.\n");
printf("WARNING: wallet.dat is only %llu bytes - possibly corrupt!\n", (unsigned long long)wsize);
}
AutoBackupWallet(walletPath);
}
}
DBErrors nLoadWalletRet = pwalletMain->LoadWallet(fFirstRun);
if (nLoadWalletRet != DB_LOAD_OK)
{
+38
View File
@@ -16,6 +16,40 @@ namespace fs = boost::filesystem;
static uint64_t nAccountingEntryNumber = 0;
extern bool fWalletUnlockStakingOnly;
//
// Auto-backup wallet before flush/rewrite operations.
// Copies wallet.dat to wallet.dat.auto.bak if the backup is older than the wallet.
// Returns true if backup was created or already up to date.
//
bool AutoBackupWallet(const fs::path& walletPath)
{
fs::path backupPath = walletPath.string() + ".auto.bak";
try {
// Only back up if wallet exists and is non-trivial (>1KB)
if (!fs::exists(walletPath))
return true;
uintmax_t walletSize = fs::file_size(walletPath);
if (walletSize < 1024) {
printf("AutoBackupWallet: wallet.dat is only %llu bytes (possibly corrupt), skipping auto-backup\n",
(unsigned long long)walletSize);
return false;
}
// Skip if backup exists and is same size (already backed up this version)
if (fs::exists(backupPath)) {
uintmax_t backupSize = fs::file_size(backupPath);
if (backupSize == walletSize)
return true;
}
fs::copy_file(walletPath, backupPath, fs::copy_options::overwrite_existing);
printf("AutoBackupWallet: backed up wallet.dat (%llu bytes) to wallet.dat.auto.bak\n",
(unsigned long long)walletSize);
return true;
} catch (const fs::filesystem_error& e) {
printf("AutoBackupWallet: failed - %s\n", e.what());
return false;
}
}
//
// CWalletDB
//
@@ -580,6 +614,10 @@ void ThreadFlushWalletDB(void* parg)
nLastFlushed = nWalletDBUpdated;
int64_t nStart = GetTimeMillis();
// Auto-backup before flush (protects against corruption)
fs::path walletPath = GetDataDir() / strFile;
AutoBackupWallet(walletPath);
// Flush wallet.dat so it's self contained
bitdb.CloseDb(strFile);
bitdb.CheckpointLSN(strFile);