From 568a0cab1aa9dfd50afa41d79d529e43160ba97a Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 31 Oct 2023 11:13:26 -0600 Subject: [PATCH] refactor wallets.dart --- lib/services/wallets.dart | 370 +++++++++------------------------ lib/wallets/wallet/wallet.dart | 11 +- 2 files changed, 105 insertions(+), 276 deletions(-) diff --git a/lib/services/wallets.dart b/lib/services/wallets.dart index 8df8d2209..31b29cd8d 100644 --- a/lib/services/wallets.dart +++ b/lib/services/wallets.dart @@ -8,165 +8,36 @@ * */ -import 'package:flutter/foundation.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:isar/isar.dart'; import 'package:stackwallet/db/hive/db.dart'; -import 'package:stackwallet/models/node_model.dart'; -import 'package:stackwallet/services/coins/coin_service.dart'; -import 'package:stackwallet/services/coins/manager.dart'; +import 'package:stackwallet/db/isar/main_db.dart'; import 'package:stackwallet/services/node_service.dart'; import 'package:stackwallet/services/transaction_notification_tracker.dart'; -import 'package:stackwallet/services/wallets_service.dart'; -import 'package:stackwallet/utilities/default_nodes.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/sync_type_enum.dart'; -import 'package:stackwallet/utilities/listenable_list.dart'; -import 'package:stackwallet/utilities/listenable_map.dart'; import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/utilities/prefs.dart'; -import 'package:tuple/tuple.dart'; +import 'package:stackwallet/wallets/isar_models/wallet_info.dart'; +import 'package:stackwallet/wallets/wallet/wallet.dart'; -final ListenableList> _nonFavorites = - ListenableList(); -ListenableList> get nonFavorites => - _nonFavorites; - -final ListenableList> _favorites = - ListenableList(); -ListenableList> get favorites => _favorites; - -class Wallets extends ChangeNotifier { +class Wallets { Wallets._private(); - @override - dispose() { - //todo: check if print needed - // debugPrint("Wallets dispose was called!!"); - super.dispose(); - } - static final Wallets _sharedInstance = Wallets._private(); static Wallets get sharedInstance => _sharedInstance; - late WalletsService walletsService; late NodeService nodeService; + late MainDB mainDB; - // mirrored maps for access to reading managers without using riverpod ref - static final ListenableMap> - _managerProviderMap = ListenableMap(); - static final ListenableMap _managerMap = ListenableMap(); - - bool get hasWallets => _managerProviderMap.isNotEmpty; - - List> get managerProviders => - _managerProviderMap.values.toList(growable: false); - List get managers => _managerMap.values.toList(growable: false); - - List getWalletIdsFor({required Coin coin}) { - final List result = []; - for (final manager in _managerMap.values) { - if (manager.coin == coin) { - result.add(manager.walletId); - } - } - return result; - } - - List>>> - getManagerProvidersByCoin() { - Map>> map = {}; - for (final manager in _managerMap.values) { - if (map[manager.coin] == null) { - map[manager.coin] = []; - } - map[manager.coin]!.add(_managerProviderMap[manager.walletId] - as ChangeNotifierProvider); - } - final List>>> result = []; - - for (final coin in Coin.values) { - if (map[coin] != null) { - result.add(Tuple2(coin, map[coin]!)); - } - } - - return result; - } - - List> getManagerProvidersForCoin(Coin coin) { - List> result = []; - for (final manager in _managerMap.values) { - if (manager.coin == coin) { - result.add(_managerProviderMap[manager.walletId] - as ChangeNotifierProvider); - } - } - return result; - } - - ChangeNotifierProvider getManagerProvider(String walletId) { - return _managerProviderMap[walletId] as ChangeNotifierProvider; - } - - Manager getManager(String walletId) { - return _managerMap[walletId] as Manager; - } - - void addWallet({required String walletId, required Manager manager}) { - _managerMap.add(walletId, manager, true); - _managerProviderMap.add( - walletId, ChangeNotifierProvider((_) => manager), true); - - if (manager.isFavorite) { - _favorites.add( - _managerProviderMap[walletId] as ChangeNotifierProvider, - false); - } else { - _nonFavorites.add( - _managerProviderMap[walletId] as ChangeNotifierProvider, - false); - } - - notifyListeners(); - } - - void removeWallet({required String walletId}) { - if (_managerProviderMap[walletId] == null) { - Logging.instance.log( - "Wallets.removeWallet($walletId) failed. ManagerProvider with $walletId not found!", - level: LogLevel.Warning); - return; - } - - final provider = _managerProviderMap[walletId]!; - - // in both non and favorites for removal - _favorites.remove(provider, true); - _nonFavorites.remove(provider, true); - - _managerProviderMap.remove(walletId, true); - _managerMap.remove(walletId, true)!.exitCurrentWallet(); - - notifyListeners(); - } + bool get hasWallets => _wallets.isNotEmpty; static bool hasLoaded = false; - Future _initLinearly( - List> tuples, - ) async { - for (final tuple in tuples) { - await tuple.item1.initializeExisting(); - if (tuple.item2 && !tuple.item1.shouldAutoSync) { - tuple.item1.shouldAutoSync = true; - } - } - } + final Map _wallets = {}; - static int _count = 0; - Future load(Prefs prefs) async { - //todo: check if print needed - // debugPrint("++++++++++++++ Wallets().load() called: ${++_count} times"); + Wallet getWallet(String walletId) => _wallets[walletId]!; + + Future load(Prefs prefs, MainDB mainDB) async { if (hasLoaded) { return; } @@ -175,18 +46,22 @@ class Wallets extends ChangeNotifier { // clear out any wallet hive boxes where the wallet was deleted in previous app run for (final walletId in DB.instance .values(boxName: DB.boxNameWalletsToDeleteOnStart)) { - await DB.instance.deleteBoxFromDisk(boxName: walletId); + await mainDB.isar.writeTxn(() async => await mainDB.isar.walletInfo + .where() + .walletIdEqualTo(walletId) + .deleteAll()); } // clear list await DB.instance .deleteAll(boxName: DB.boxNameWalletsToDeleteOnStart); - final map = await walletsService.walletNames; + final walletInfoList = await mainDB.isar.walletInfo.where().findAll(); + if (walletInfoList.isEmpty) { + return; + } - List> walletInitFutures = []; - List> walletsToInitLinearly = []; - - final favIdList = await walletsService.getFavoriteWalletIds(); + List> walletInitFutures = []; + List<({Wallet wallet, bool shouldAutoSync})> walletsToInitLinearly = []; List walletIdsToEnableAutoSync = []; bool shouldAutoSyncAll = false; @@ -202,92 +77,47 @@ class Wallets extends ChangeNotifier { break; } - for (final entry in map.entries) { + for (final walletInfo in walletInfoList) { try { - final walletId = entry.value.walletId; - - late final bool isVerified; - try { - isVerified = - await walletsService.isMnemonicVerified(walletId: walletId); - } catch (e, s) { - Logging.instance.log("$e $s", level: LogLevel.Warning); - isVerified = false; - } - Logging.instance.log( - "LOADING WALLET: ${entry.value.toString()} IS VERIFIED: $isVerified", - level: LogLevel.Info); - if (isVerified) { - if (_managerMap[walletId] == null && - _managerProviderMap[walletId] == null) { - final coin = entry.value.coin; - NodeModel node = nodeService.getPrimaryNodeFor(coin: coin) ?? - DefaultNodes.getNodeFor(coin); - // ElectrumXNode? node = await nodeService.getCurrentNode(coin: coin); + "LOADING WALLET: ${walletInfo.name}:${walletInfo.walletId} " + "IS VERIFIED: ${walletInfo.isMnemonicVerified}", + level: LogLevel.Info, + ); - // folowing shouldn't be needed as the defaults get saved on init - // if (node == null) { - // node = DefaultNodes.getNodeFor(coin); - // - // // save default node - // nodeService.add(node, false); - // } + if (walletInfo.isMnemonicVerified) { + // TODO: integrate this into the new wallets somehow? + // requires some thinking + final txTracker = + TransactionNotificationTracker(walletId: walletInfo.walletId); - final txTracker = - TransactionNotificationTracker(walletId: walletId); + final wallet = await Wallet.load( + walletId: walletInfo.walletId, + mainDB: mainDB, + secureStorageInterface: nodeService.secureStorageInterface, + nodeService: nodeService, + prefs: prefs, + ); - final failovers = nodeService.failoverNodesFor(coin: coin); + final shouldSetAutoSync = shouldAutoSyncAll || + walletIdsToEnableAutoSync.contains(walletInfo.walletId); - // load wallet - final wallet = CoinServiceAPI.from( - coin, - walletId, - entry.value.name, - nodeService.secureStorageInterface, - node, - txTracker, - prefs, - failovers, - ); - - final manager = Manager(wallet); - - final shouldSetAutoSync = shouldAutoSyncAll || - walletIdsToEnableAutoSync.contains(manager.walletId); - - if (manager.coin == Coin.monero || manager.coin == Coin.wownero) { - // walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync)); - } else { - walletInitFutures.add(manager.initializeExisting().then((value) { - if (shouldSetAutoSync) { - manager.shouldAutoSync = true; - } - })); - } - - _managerMap.add(walletId, manager, false); - - final managerProvider = - ChangeNotifierProvider((_) => manager); - _managerProviderMap.add(walletId, managerProvider, false); - - final favIndex = favIdList.indexOf(walletId); - - if (favIndex == -1) { - _nonFavorites.add(managerProvider, true); - } else { - // it is a favorite - if (favIndex >= _favorites.length) { - _favorites.add(managerProvider, true); - } else { - _favorites.insert(favIndex, managerProvider, true); + if (walletInfo.coin == Coin.monero || + walletInfo.coin == Coin.wownero) { + // walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync)); + } else { + walletInitFutures.add(wallet.init().then((_) { + if (shouldSetAutoSync) { + wallet.shouldAutoSync = true; } - } + })); } + + _wallets[wallet.walletId] = wallet; } else { // wallet creation was not completed by user so we remove it completely - await walletsService.deleteWallet(entry.value.name, false); + await _deleteWallet(walletInfo.walletId); + // await walletsService.deleteWallet(walletInfo.name, false); } } catch (e, s) { Logging.instance.log("$e $s", level: LogLevel.Fatal); @@ -300,22 +130,19 @@ class Wallets extends ChangeNotifier { _initLinearly(walletsToInitLinearly), ...walletInitFutures, ]); - notifyListeners(); } else if (walletInitFutures.isNotEmpty) { await Future.wait(walletInitFutures); - notifyListeners(); } else if (walletsToInitLinearly.isNotEmpty) { await _initLinearly(walletsToInitLinearly); - notifyListeners(); } } Future loadAfterStackRestore( - Prefs prefs, List managers) async { - List> walletInitFutures = []; - List> walletsToInitLinearly = []; - - final favIdList = await walletsService.getFavoriteWalletIds(); + Prefs prefs, + List wallets, + ) async { + List> walletInitFutures = []; + List<({Wallet wallet, bool shouldAutoSync})> walletsToInitLinearly = []; List walletIdsToEnableAutoSync = []; bool shouldAutoSyncAll = false; @@ -331,53 +158,32 @@ class Wallets extends ChangeNotifier { break; } - for (final manager in managers) { - final walletId = manager.walletId; + for (final wallet in wallets) { + Logging.instance.log( + "LOADING WALLET: ${wallet.walletInfo.name}:${wallet.walletId} IS VERIFIED: ${wallet.walletInfo.isMnemonicVerified}", + level: LogLevel.Info, + ); - final isVerified = - await walletsService.isMnemonicVerified(walletId: walletId); - //todo: check if print needed - // debugPrint( - // "LOADING RESTORED WALLET: ${manager.walletName} ${manager.walletId} IS VERIFIED: $isVerified"); + if (wallet.walletInfo.isMnemonicVerified) { + final shouldSetAutoSync = shouldAutoSyncAll || + walletIdsToEnableAutoSync.contains(wallet.walletId); - if (isVerified) { - if (_managerMap[walletId] == null && - _managerProviderMap[walletId] == null) { - final shouldSetAutoSync = shouldAutoSyncAll || - walletIdsToEnableAutoSync.contains(manager.walletId); - - if (manager.coin == Coin.monero || manager.coin == Coin.wownero) { - // walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync)); - } else { - walletInitFutures.add(manager.initializeExisting().then((value) { - if (shouldSetAutoSync) { - manager.shouldAutoSync = true; - } - })); - } - - _managerMap.add(walletId, manager, false); - - final managerProvider = - ChangeNotifierProvider((_) => manager); - _managerProviderMap.add(walletId, managerProvider, false); - - final favIndex = favIdList.indexOf(walletId); - - if (favIndex == -1) { - _nonFavorites.add(managerProvider, true); - } else { - // it is a favorite - if (favIndex >= _favorites.length) { - _favorites.add(managerProvider, true); - } else { - _favorites.insert(favIndex, managerProvider, true); + if (wallet.walletInfo.coin == Coin.monero || + wallet.walletInfo.coin == Coin.wownero) { + // walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync)); + } else { + walletInitFutures.add(wallet.init().then((value) { + if (shouldSetAutoSync) { + wallet.shouldAutoSync = true; } - } + })); } + + _wallets[wallet.walletId] = wallet; } else { // wallet creation was not completed by user so we remove it completely - await walletsService.deleteWallet(manager.walletName, false); + await _deleteWallet(wallet.walletId); + // await walletsService.deleteWallet(walletInfo.name, false); } } @@ -386,13 +192,29 @@ class Wallets extends ChangeNotifier { _initLinearly(walletsToInitLinearly), ...walletInitFutures, ]); - notifyListeners(); } else if (walletInitFutures.isNotEmpty) { await Future.wait(walletInitFutures); - notifyListeners(); } else if (walletsToInitLinearly.isNotEmpty) { await _initLinearly(walletsToInitLinearly); - notifyListeners(); } } + + Future _initLinearly( + List<({Wallet wallet, bool shouldAutoSync})> dataList, + ) async { + for (final data in dataList) { + await data.wallet.init(); + if (data.shouldAutoSync && !data.wallet.shouldAutoSync) { + data.wallet.shouldAutoSync = true; + } + } + } + + Future _deleteWallet(String walletId) async { + // TODO proper clean up of other wallet data in addition to the following + await mainDB.isar.writeTxn(() async => await mainDB.isar.walletInfo + .where() + .walletIdEqualTo(walletId) + .deleteAll()); + } } diff --git a/lib/wallets/wallet/wallet.dart b/lib/wallets/wallet/wallet.dart index a155b6f41..36852a746 100644 --- a/lib/wallets/wallet/wallet.dart +++ b/lib/wallets/wallet/wallet.dart @@ -328,6 +328,8 @@ abstract class Wallet { Future pingCheck(); + Future updateNode(); + Future updateTransactions(); Future updateUTXOs(); Future updateBalance(); @@ -436,7 +438,12 @@ abstract class Wallet { } } - Future exit() async {} + Future exit() async { + // TODO: + } - Future updateNode(); + Future init() async { + // TODO: make sure subclasses override this if they require some set up + // especially xmr/wow/epiccash + } }