Better handle corrupted wallets (#1384)

* Fix exchanges not showing * Fix button text on Monero receive screen * Temp fix for ERC20 and SPL tokens not having raw value * fallback to other wallets if the current wallet is corrupted so we give user access to the app --------- Co-authored-by: tuxsudo <tuxsudo@tux.pizza>

Omar Hatem committed Apr 26, 2024 at 22:13 UTC f3160860b1796ef0abe41299eb954ce26c1b899b
1 file changed +44 -17
lib/core/wallet_loading_service.dart
+44 -17
@@ -1,27 +1,28 @@
1 import 'package:cake_wallet/core/generate_wallet_password.dart';
2 import 'package:cake_wallet/core/key_service.dart';
3 import 'package:cake_wallet/entities/preferences_key.dart';
4 +import 'package:cake_wallet/utils/exception_handler.dart';
5 +import 'package:cw_core/cake_hive.dart';
6 import 'package:cw_core/wallet_base.dart';
7 +import 'package:cw_core/wallet_info.dart';
8 import 'package:cw_core/wallet_service.dart';
9 import 'package:cw_core/wallet_type.dart';
10 +import 'package:flutter/material.dart';
11 import 'package:shared_preferences/shared_preferences.dart';
12
13 class WalletLoadingService {
10 - WalletLoadingService(
11 - this.sharedPreferences, this.keyService, this.walletServiceFactory);
14 + WalletLoadingService(this.sharedPreferences, this.keyService, this.walletServiceFactory);
15
16 final SharedPreferences sharedPreferences;
17 final KeyService keyService;
18 final WalletService Function(WalletType type) walletServiceFactory;
19
17 - Future<void> renameWallet(
18 - WalletType type, String name, String newName) async {
20 + Future<void> renameWallet(WalletType type, String name, String newName) async {
21 final walletService = walletServiceFactory.call(type);
22 final password = await keyService.getWalletPassword(walletName: name);
23
24 // Save the current wallet's password to the new wallet name's key
23 - await keyService.saveWalletPassword(
24 - walletName: newName, password: password);
25 + await keyService.saveWalletPassword(walletName: newName, password: password);
26 // Delete previous wallet name from keyService to keep only new wallet's name
27 // otherwise keeps duplicate (old and new names)
28 await keyService.deleteWalletPassword(walletName: name);
@@ -38,15 +39,43 @@ class WalletLoadingService {
39 }
40
41 Future<WalletBase> load(WalletType type, String name) async {
41 - final walletService = walletServiceFactory.call(type);
42 - final password = await keyService.getWalletPassword(walletName: name);
43 - final wallet = await walletService.openWallet(name, password);
42 + try {
43 + final walletService = walletServiceFactory.call(type);
44 + final password = await keyService.getWalletPassword(walletName: name);
45 + final wallet = await walletService.openWallet(name, password);
46
45 - if (type == WalletType.monero) {
46 - await updateMoneroWalletPassword(wallet);
47 - }
47 + if (type == WalletType.monero) {
48 + await updateMoneroWalletPassword(wallet);
49 + }
50 +
51 + return wallet;
52 + } catch (error, stack) {
53 + ExceptionHandler.onError(FlutterErrorDetails(exception: error, stack: stack));
54 +
55 + // try opening another wallet that is not corrupted to give user access to the app
56 + final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
57
49 - return wallet;
58 + for (var walletInfo in walletInfoSource.values) {
59 + try {
60 + final walletService = walletServiceFactory.call(walletInfo.type);
61 + final password = await keyService.getWalletPassword(walletName: walletInfo.name);
62 + final wallet = await walletService.openWallet(walletInfo.name, password);
63 +
64 + if (walletInfo.type == WalletType.monero) {
65 + await updateMoneroWalletPassword(wallet);
66 + }
67 +
68 + await sharedPreferences.setString(PreferencesKey.currentWalletName, wallet.name);
69 + await sharedPreferences.setInt(
70 + PreferencesKey.currentWalletType, serializeToInt(wallet.type));
71 +
72 + return wallet;
73 + } catch (_) {}
74 + }
75 +
76 + // if all user's wallets are corrupted throw exception
77 + throw error;
78 + }
79 }
80
81 Future<void> updateMoneroWalletPassword(WalletBase wallet) async {
@@ -61,11 +90,9 @@ class WalletLoadingService {
90 // Save new generated password with backup key for case where
91 // wallet will change password, but it will fail to update in secure storage
92 final bakWalletName = '#__${wallet.name}_bak__#';
64 - await keyService.saveWalletPassword(
65 - walletName: bakWalletName, password: password);
93 + await keyService.saveWalletPassword(walletName: bakWalletName, password: password);
94 await wallet.changePassword(password);
67 - await keyService.saveWalletPassword(
68 - walletName: wallet.name, password: password);
95 + await keyService.saveWalletPassword(walletName: wallet.name, password: password);
96 isPasswordUpdated = true;
97 await sharedPreferences.setBool(key, isPasswordUpdated);
98 }