diff --git a/src/db.h b/src/db.h index 40ffeb9..11f9850 100644 --- a/src/db.h +++ b/src/db.h @@ -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 diff --git a/src/init.cpp b/src/init.cpp index a19de55..c4414ca 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -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) { diff --git a/src/walletdb.cpp b/src/walletdb.cpp index afc73b3..1a865eb 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -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);