fix(wallet-switch): close gaps found verifying clusters A/B/C
Adversarial verification of the audit fixes found real gaps in them: - HIGH throw-safety: the switch worker and the encryption-restart worker set daemon_restarting_=true but reset it only on their normal/early-return paths — a throw from stop/startEmbeddedDaemon left the flag stuck true, wedging reconnect and every future switch/rescan/encryption. Both now reset it on all paths (try/catch); a throw during a switch is treated as a failed switch and triggers the revert. - HIGH residual chat leak: resetChatSession() cleared the flags but an already- posted z_exportmnemonic worker job still held wallet A's secret, and its completion callback (guarded only by isLocked(), false for an unencrypted wallet) would provision A's identity under B. Add a chat_session_generation_ epoch bumped on every wallet change; the fetch captures it and its callback discards the (previous-wallet) secret if the epoch no longer matches. - LOW vault-scope collision: the per-wallet vault tag was a lossy char-substitution (two distinct files could map to one vault). Append an 8-hex FNV-1a of the raw filename so distinct wallets never share a vault. +unit test. - LOW seed-adopt: removeVault() on adopt so the legacy wallet's PIN passphrase isn't left associated with the new seed wallet (same file name). - LOW: clear lock_unlock_in_progress_ on switch too. Deferred (documented): the 1.5s start grace can't catch a wallet that fails LATE in daemon init (the existing crash-wedge detection still applies); beginShutdown's join of the switch task can briefly freeze the UI during quit (necessary to avoid orphaning the daemon). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include "../util/logger.h"
|
||||
@@ -45,13 +46,20 @@ SecureVault::~SecureVault() = default;
|
||||
|
||||
void SecureVault::setWalletScope(const std::string& walletFile) {
|
||||
// The default wallet keeps the legacy global "vault.dat" (empty scope) for backward compatibility;
|
||||
// any other wallet gets a filesystem-safe per-wallet tag so its vault is separate.
|
||||
// any other wallet gets a per-wallet tag. A readable sanitized prefix PLUS an 8-hex FNV-1a of the
|
||||
// RAW filename — the hash disambiguates so two distinct files can never share a vault even if their
|
||||
// sanitized forms collide (e.g. "wallet foo.dat" vs "wallet_foo.dat").
|
||||
if (walletFile.empty() || walletFile == "wallet.dat") { scope_.clear(); return; }
|
||||
std::string s;
|
||||
s.reserve(walletFile.size());
|
||||
for (unsigned char c : walletFile)
|
||||
s += (std::isalnum(c) || c == '-' || c == '_') ? static_cast<char>(c) : '_';
|
||||
scope_ = s;
|
||||
uint64_t h = 1469598103934665603ULL; // FNV-1a of the raw name
|
||||
for (unsigned char c : walletFile) { h ^= c; h *= 1099511628211ULL; }
|
||||
char suffix[9];
|
||||
std::snprintf(suffix, sizeof(suffix), "%08x", static_cast<unsigned>(h & 0xffffffffu));
|
||||
if (s.size() > 48) s.resize(48);
|
||||
scope_ = s + "-" + suffix;
|
||||
}
|
||||
|
||||
std::string SecureVault::getVaultPath() const {
|
||||
|
||||
Reference in New Issue
Block a user