From 540ed06974ff6b05364d4922895288744f076f6b Mon Sep 17 00:00:00 2001 From: Nsable-sh Date: Thu, 13 Aug 2026 18:11:30 -0300 Subject: wallet: add storage sanity check --- src/walletcmd.cpp | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) (limited to 'src/walletcmd.cpp') diff --git a/src/walletcmd.cpp b/src/walletcmd.cpp index 6f8258d..265febe 100644 --- a/src/walletcmd.cpp +++ b/src/walletcmd.cpp @@ -13,6 +13,10 @@ #include "bip32.h" #include "walletcmd.h" +#ifndef _WIN32 +#include +#endif + #undef printf // How far ahead to look for used addresses when restoring. @@ -658,6 +662,139 @@ static bool WriteAuditTextFile(const std::string& strPath, return true; } +static bool DirectoryHasFileWithPrefixLocal(const std::string& strDir, + const std::string& strPrefix) +{ +#ifdef _WIN32 + std::string pattern = strDir + "/*"; + WIN32_FIND_DATAA findData; + HANDLE hFind = FindFirstFileA(pattern.c_str(), &findData); + if (hFind == INVALID_HANDLE_VALUE) + return false; + bool fFound = false; + do + { + if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) + { + std::string name = findData.cFileName; + if (name.compare(0, strPrefix.size(), strPrefix) == 0) + { + fFound = true; + break; + } + } + } + while (FindNextFileA(hFind, &findData)); + FindClose(hFind); + return fFound; +#else + DIR* dir = opendir(strDir.c_str()); + if (!dir) + return false; + bool fFound = false; + struct dirent* ent; + while ((ent = readdir(dir)) != NULL) + { + std::string name = ent->d_name; + if (name.compare(0, strPrefix.size(), strPrefix) == 0) + { + fFound = true; + break; + } + } + closedir(dir); + return fFound; +#endif +} + +static void AddWalletStorageFailure(std::vector& vFailures, + const std::string& strFailure) +{ + vFailures.push_back(strFailure); +} + +static void BuildWalletStorageSanityFailures(const WalletStorageAuditCounts& counts, + std::vector& vFailures) +{ + bool fPlainHD = counts.nPlainHDMaster > 0 || counts.nPlainHDChainCode > 0; + bool fCryptedHD = counts.nCryptedHDMaster > 0 || counts.nCryptedHDChainCode > 0; + bool fEncryptedRecords = counts.nEncryptedKeys > 0 || + counts.nMasterKeys > 0 || + fCryptedHD; + + if (counts.nMalformed > 0) + AddWalletStorageFailure(vFailures, "wallet.dat contains malformed records"); + if (counts.nUnknown > 0) + AddWalletStorageFailure(vFailures, "wallet.dat contains unknown records"); + if ((counts.nPlainHDMaster > 0) != (counts.nPlainHDChainCode > 0)) + AddWalletStorageFailure(vFailures, "plain HD seed is incomplete"); + if ((counts.nCryptedHDMaster > 0) != (counts.nCryptedHDChainCode > 0)) + AddWalletStorageFailure(vFailures, "encrypted HD seed is incomplete"); + if (fPlainHD && fCryptedHD) + AddWalletStorageFailure(vFailures, "wallet.dat contains both plain and encrypted HD seed records"); + if (counts.nDefaultKey > 1) + AddWalletStorageFailure(vFailures, "wallet.dat contains more than one default public key record"); + if (counts.nWalletMinVersion > 1) + AddWalletStorageFailure(vFailures, "wallet.dat contains more than one minimum-version record"); + + if (fEncryptedRecords) + { + if (counts.nPlainKeys > 0) + AddWalletStorageFailure(vFailures, "encrypted wallet still contains plain private key records"); + if (fPlainHD) + AddWalletStorageFailure(vFailures, "encrypted wallet still contains plain HD seed records"); + if (counts.nMasterKeys == 0) + AddWalletStorageFailure(vFailures, "encrypted wallet has no encryption master key record"); + if (counts.nWalletMinVersion == 0) + AddWalletStorageFailure(vFailures, "encrypted wallet has no minimum-version record"); + if (DirectoryHasFileWithPrefixLocal(GetAppDir() + "/database", "log.")) + AddWalletStorageFailure(vFailures, "Berkeley DB log files are still present for an encrypted wallet"); + } +} + +int CmdWalletStorageCheck() +{ + AttachTerminal(); + + WalletStorageAuditCounts counts; + std::string strError; + if (!ReadWalletStorageCounts(counts, strError)) + { + fprintf(stderr, "Cannot check wallet storage: %s\n", strError.c_str()); + return 1; + } + + std::vector vFailures; + BuildWalletStorageSanityFailures(counts, vFailures); + + printf("Wallet storage sanity check\n"); + printf(" records total: %u\n", counts.nTotal); + printf(" encrypted records: %s\n", + (counts.nEncryptedKeys || counts.nMasterKeys || + counts.nCryptedHDMaster || counts.nCryptedHDChainCode) ? "yes" : "no"); + printf(" plain private keys: %u\n", counts.nPlainKeys); + printf(" encrypted private keys: %u\n", counts.nEncryptedKeys); + printf(" plain HD seed: %s\n", + HDSeedStorageState(counts.nPlainHDMaster, + counts.nPlainHDChainCode).c_str()); + printf(" encrypted HD seed: %s\n", + HDSeedStorageState(counts.nCryptedHDMaster, + counts.nCryptedHDChainCode).c_str()); + if (vFailures.empty()) + { + printf(" storage sanity: ok\n"); + fflush(stdout); + return 0; + } + + printf(" storage sanity: failed\n"); + for (std::vector::const_iterator it = vFailures.begin(); + it != vFailures.end(); ++it) + printf(" failure: %s\n", it->c_str()); + fflush(stdout); + return 2; +} + int CmdWalletStorageAudit(const std::string& strJsonOut) { AttachTerminal(); -- cgit v1.2.3 From b3088010726c9ab1ceeaa84357e91a8f81763fe2 Mon Sep 17 00:00:00 2001 From: Nsable-sh Date: Fri, 14 Aug 2026 08:24:17 -0300 Subject: wallet: initialize storage audit counts explicitly --- src/walletcmd.cpp | 49 ++++++++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 27 deletions(-) (limited to 'src/walletcmd.cpp') diff --git a/src/walletcmd.cpp b/src/walletcmd.cpp index 265febe..6fc8c38 100644 --- a/src/walletcmd.cpp +++ b/src/walletcmd.cpp @@ -476,33 +476,28 @@ int CmdRecoveryAudit() struct WalletStorageAuditCounts { - unsigned int nTotal; - unsigned int nMalformed; - unsigned int nUnknown; - unsigned int nVersion; - unsigned int nNames; - unsigned int nTransactions; - unsigned int nPlainKeys; - unsigned int nEncryptedKeys; - unsigned int nMasterKeys; - unsigned int nDefaultKey; - unsigned int nPlainHDMaster; - unsigned int nPlainHDChainCode; - unsigned int nCryptedHDMaster; - unsigned int nCryptedHDChainCode; - unsigned int nWalletMinVersion; - unsigned int nPool; - unsigned int nSettings; - unsigned int nHDNext; - unsigned int nHDSchema; - unsigned int nHDCoinType; - unsigned int nHDReceiveNext; - unsigned int nHDChangeNext; - - WalletStorageAuditCounts() - { - memset(this, 0, sizeof(*this)); - } + unsigned int nTotal = 0; + unsigned int nMalformed = 0; + unsigned int nUnknown = 0; + unsigned int nVersion = 0; + unsigned int nNames = 0; + unsigned int nTransactions = 0; + unsigned int nPlainKeys = 0; + unsigned int nEncryptedKeys = 0; + unsigned int nMasterKeys = 0; + unsigned int nDefaultKey = 0; + unsigned int nPlainHDMaster = 0; + unsigned int nPlainHDChainCode = 0; + unsigned int nCryptedHDMaster = 0; + unsigned int nCryptedHDChainCode = 0; + unsigned int nWalletMinVersion = 0; + unsigned int nPool = 0; + unsigned int nSettings = 0; + unsigned int nHDNext = 0; + unsigned int nHDSchema = 0; + unsigned int nHDCoinType = 0; + unsigned int nHDReceiveNext = 0; + unsigned int nHDChangeNext = 0; }; static std::string HDSeedStorageState(unsigned int nMaster, unsigned int nChain) -- cgit v1.2.3 From 33af98c042340f6431aa93b4b6f893d85f40f237 Mon Sep 17 00:00:00 2001 From: Nsable-sh Date: Fri, 14 Aug 2026 08:48:26 -0300 Subject: wallet: drop bdb log check from storage sanity --- src/walletcmd.cpp | 51 --------------------------------------------------- 1 file changed, 51 deletions(-) (limited to 'src/walletcmd.cpp') diff --git a/src/walletcmd.cpp b/src/walletcmd.cpp index 6fc8c38..ce0da02 100644 --- a/src/walletcmd.cpp +++ b/src/walletcmd.cpp @@ -13,10 +13,6 @@ #include "bip32.h" #include "walletcmd.h" -#ifndef _WIN32 -#include -#endif - #undef printf // How far ahead to look for used addresses when restoring. @@ -657,51 +653,6 @@ static bool WriteAuditTextFile(const std::string& strPath, return true; } -static bool DirectoryHasFileWithPrefixLocal(const std::string& strDir, - const std::string& strPrefix) -{ -#ifdef _WIN32 - std::string pattern = strDir + "/*"; - WIN32_FIND_DATAA findData; - HANDLE hFind = FindFirstFileA(pattern.c_str(), &findData); - if (hFind == INVALID_HANDLE_VALUE) - return false; - bool fFound = false; - do - { - if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) - { - std::string name = findData.cFileName; - if (name.compare(0, strPrefix.size(), strPrefix) == 0) - { - fFound = true; - break; - } - } - } - while (FindNextFileA(hFind, &findData)); - FindClose(hFind); - return fFound; -#else - DIR* dir = opendir(strDir.c_str()); - if (!dir) - return false; - bool fFound = false; - struct dirent* ent; - while ((ent = readdir(dir)) != NULL) - { - std::string name = ent->d_name; - if (name.compare(0, strPrefix.size(), strPrefix) == 0) - { - fFound = true; - break; - } - } - closedir(dir); - return fFound; -#endif -} - static void AddWalletStorageFailure(std::vector& vFailures, const std::string& strFailure) { @@ -742,8 +693,6 @@ static void BuildWalletStorageSanityFailures(const WalletStorageAuditCounts& cou AddWalletStorageFailure(vFailures, "encrypted wallet has no encryption master key record"); if (counts.nWalletMinVersion == 0) AddWalletStorageFailure(vFailures, "encrypted wallet has no minimum-version record"); - if (DirectoryHasFileWithPrefixLocal(GetAppDir() + "/database", "log.")) - AddWalletStorageFailure(vFailures, "Berkeley DB log files are still present for an encrypted wallet"); } } -- cgit v1.2.3