Keep the Monero wallet out of iCloud backups

This commit is contained in:
2026-09-19 22:54:58 +08:00
parent 807388ea6b
commit 1a23c730c6
@@ -14,7 +14,22 @@ static std::string walletPath(NSString *name) {
return std::string([dir stringByAppendingPathComponent:name].UTF8String); return std::string([dir stringByAppendingPathComponent:name].UTF8String);
} }
// Keep wallet files out of iCloud/iTunes backups: the seed in the Keychain is
// device-only and the file is rebuildable from it, so backing it up would only
// consume the user's quota (it peaks near 120 MB while a wallet restored from
// genesis scans). Must run after every write, since wallet2 rewrites these
// files on each save.
static void excludeWalletFilesFromBackup(const std::string &walletPathString) {
NSString *base = [NSString stringWithUTF8String:walletPathString.c_str()];
if (base.length == 0) return;
for (NSString *suffix in @[ @"", @".keys", @".address.txt", @".mms" ]) {
NSURL *url = [NSURL fileURLWithPath:[base stringByAppendingString:suffix]];
[url setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
}
}
static monero_wallet_full *g_wallet = nullptr; static monero_wallet_full *g_wallet = nullptr;
static std::string g_walletPath;
static NSString *g_daemonURL = @"xmr-node.cakewallet.com:18081"; static NSString *g_daemonURL = @"xmr-node.cakewallet.com:18081";
static NSArray<NSString *> *g_fallbackDaemons = nil; static NSArray<NSString *> *g_fallbackDaemons = nil;
static std::atomic<bool> s_cachedConnected(false); static std::atomic<bool> s_cachedConnected(false);
@@ -82,6 +97,8 @@ static void connectDaemon(monero_wallet_full *w) {
connectDaemon(g_wallet); connectDaemon(g_wallet);
resp[@"address"] = [NSString stringWithUTF8String:g_wallet->get_address(0, 0).c_str()]; resp[@"address"] = [NSString stringWithUTF8String:g_wallet->get_address(0, 0).c_str()];
try { resp[@"mnemonic"] = [NSString stringWithUTF8String:g_wallet->get_seed().c_str()]; } catch (...) {} try { resp[@"mnemonic"] = [NSString stringWithUTF8String:g_wallet->get_seed().c_str()]; } catch (...) {}
g_walletPath = path;
excludeWalletFilesFromBackup(g_walletPath);
} else { } else {
monero_wallet_config config; monero_wallet_config config;
config.m_path = path; config.m_path = path;
@@ -94,6 +111,8 @@ static void connectDaemon(monero_wallet_full *w) {
connectDaemon(w); connectDaemon(w);
resp[@"address"] = [NSString stringWithUTF8String:w->get_address(0, 0).c_str()]; resp[@"address"] = [NSString stringWithUTF8String:w->get_address(0, 0).c_str()];
resp[@"mnemonic"] = [NSString stringWithUTF8String:w->get_seed().c_str()]; resp[@"mnemonic"] = [NSString stringWithUTF8String:w->get_seed().c_str()];
g_walletPath = path;
excludeWalletFilesFromBackup(g_walletPath);
} }
} else if ([op isEqualToString:@"openWallet"] || [op isEqualToString:@"restoreWallet"]) { } else if ([op isEqualToString:@"openWallet"] || [op isEqualToString:@"restoreWallet"]) {
if (g_wallet) { if (g_wallet) {
@@ -118,6 +137,8 @@ static void connectDaemon(monero_wallet_full *w) {
g_wallet = monero_wallet_full::create_wallet(config, nullptr); g_wallet = monero_wallet_full::create_wallet(config, nullptr);
} }
connectDaemon(g_wallet); connectDaemon(g_wallet);
g_walletPath = walletPath(req[@"name"]);
excludeWalletFilesFromBackup(g_walletPath);
resp[@"address"] = [NSString stringWithUTF8String:g_wallet->get_address(0, 0).c_str()]; resp[@"address"] = [NSString stringWithUTF8String:g_wallet->get_address(0, 0).c_str()];
} else if ([op isEqualToString:@"getSeed"]) { } else if ([op isEqualToString:@"getSeed"]) {
if (g_wallet) { if (g_wallet) {
@@ -145,12 +166,14 @@ static void connectDaemon(monero_wallet_full *w) {
delete g_wallet; delete g_wallet;
g_wallet = nullptr; g_wallet = nullptr;
} }
g_walletPath.clear();
} else if ([op isEqualToString:@"saveWallet"]) { } else if ([op isEqualToString:@"saveWallet"]) {
// monero-cpp never persists while syncing, so scan progress survives a // monero-cpp never persists while syncing, so scan progress survives a
// restart only if this is called explicitly. save() pauses the sync // restart only if this is called explicitly. save() pauses the sync
// thread while it writes. // thread while it writes.
if (g_wallet) { if (g_wallet) {
g_wallet->save(); g_wallet->save();
excludeWalletFilesFromBackup(g_walletPath);
resp[@"saved"] = @YES; resp[@"saved"] = @YES;
} else { } else {
resp[@"error"] = @"No wallet open"; resp[@"error"] = @"No wallet open";