From 0ef1726a00253c01eb9bfd78825ebe04ea39e2d7 Mon Sep 17 00:00:00 2001 From: Julian Date: Wed, 26 Jun 2024 12:10:32 -0600 Subject: [PATCH] fix firo spark cache being shared with test net --- lib/db/sqlite/firo_cache.dart | 79 +++++++++++-------- lib/db/sqlite/firo_cache_coordinator.dart | 56 ++++++++----- lib/db/sqlite/firo_cache_worker.dart | 7 +- .../wallet_settings_view.dart | 4 +- .../spark_info.dart | 8 +- .../wallet_settings_wallet_settings_view.dart | 1 + .../wallet_view/desktop_wallet_view.dart | 4 +- .../more_features/more_features_dialog.dart | 19 +++-- lib/route_generator.dart | 19 +++-- lib/wallets/wallet/impl/firo_wallet.dart | 3 + .../spark_interface.dart | 17 +++- 11 files changed, 143 insertions(+), 74 deletions(-) diff --git a/lib/db/sqlite/firo_cache.dart b/lib/db/sqlite/firo_cache.dart index b30777643..eac511aaa 100644 --- a/lib/db/sqlite/firo_cache.dart +++ b/lib/db/sqlite/firo_cache.dart @@ -12,6 +12,7 @@ import '../../electrumx_rpc/electrumx_client.dart'; import '../../utilities/extensions/extensions.dart'; import '../../utilities/logger.dart'; import '../../utilities/stack_file_system.dart'; +import '../../wallets/crypto_currency/crypto_currency.dart'; part 'firo_cache_coordinator.dart'; part 'firo_cache_reader.dart'; @@ -31,29 +32,39 @@ void _debugLog(Object? object) { abstract class _FiroCache { static const int _setCacheVersion = 1; static const int _tagsCacheVersion = 2; - static const String sparkSetCacheFileName = - "spark_set_v$_setCacheVersion.sqlite3"; - static const String sparkUsedTagsCacheFileName = - "spark_tags_v$_tagsCacheVersion.sqlite3"; - static Database? _setCacheDB; - static Database? _usedTagsCacheDB; - static Database get setCacheDB { - if (_setCacheDB == null) { + static final networks = [ + CryptoCurrencyNetwork.main, + CryptoCurrencyNetwork.test, + ]; + + static String sparkSetCacheFileName(CryptoCurrencyNetwork network) => + network == CryptoCurrencyNetwork.main + ? "spark_set_v$_setCacheVersion.sqlite3" + : "spark_set_v${_setCacheVersion}_${network.name}.sqlite3"; + static String sparkUsedTagsCacheFileName(CryptoCurrencyNetwork network) => + network == CryptoCurrencyNetwork.main + ? "spark_tags_v$_tagsCacheVersion.sqlite3" + : "spark_tags_v${_tagsCacheVersion}_${network.name}.sqlite3"; + + static final Map _setCacheDB = {}; + static final Map _usedTagsCacheDB = {}; + static Database setCacheDB(CryptoCurrencyNetwork network) { + if (_setCacheDB[network] == null) { throw Exception( "FiroCache.init() must be called before accessing FiroCache.db!", ); } - return _setCacheDB!; + return _setCacheDB[network]!; } - static Database get usedTagsCacheDB { - if (_usedTagsCacheDB == null) { + static Database usedTagsCacheDB(CryptoCurrencyNetwork network) { + if (_usedTagsCacheDB[network] == null) { throw Exception( "FiroCache.init() must be called before accessing FiroCache.db!", ); } - return _usedTagsCacheDB!; + return _usedTagsCacheDB[network]!; } static Future? _initFuture; @@ -63,30 +74,34 @@ abstract class _FiroCache { final sqliteDir = await StackFileSystem.applicationFiroCacheSQLiteDirectory(); - final sparkSetCacheFile = File("${sqliteDir.path}/$sparkSetCacheFileName"); - final sparkUsedTagsCacheFile = - File("${sqliteDir.path}/$sparkUsedTagsCacheFileName"); + for (final network in networks) { + final sparkSetCacheFile = + File("${sqliteDir.path}/${sparkSetCacheFileName(network)}"); - if (!(await sparkSetCacheFile.exists())) { - await _createSparkSetCacheDb(sparkSetCacheFile.path); - } - if (!(await sparkUsedTagsCacheFile.exists())) { - await _createSparkUsedTagsCacheDb(sparkUsedTagsCacheFile.path); - } + final sparkUsedTagsCacheFile = + File("${sqliteDir.path}/${sparkUsedTagsCacheFileName(network)}"); - _setCacheDB = sqlite3.open( - sparkSetCacheFile.path, - mode: OpenMode.readWrite, - ); - _usedTagsCacheDB = sqlite3.open( - sparkUsedTagsCacheFile.path, - mode: OpenMode.readWrite, - ); + if (!(await sparkSetCacheFile.exists())) { + await _createSparkSetCacheDb(sparkSetCacheFile.path); + } + if (!(await sparkUsedTagsCacheFile.exists())) { + await _createSparkUsedTagsCacheDb(sparkUsedTagsCacheFile.path); + } + + _setCacheDB[network] = sqlite3.open( + sparkSetCacheFile.path, + mode: OpenMode.readWrite, + ); + _usedTagsCacheDB[network] = sqlite3.open( + sparkUsedTagsCacheFile.path, + mode: OpenMode.readWrite, + ); + } } - static Future _deleteAllCache() async { + static Future _deleteAllCache(CryptoCurrencyNetwork network) async { final start = DateTime.now(); - setCacheDB.execute( + setCacheDB(network).execute( """ DELETE FROM SparkSet; DELETE FROM SparkCoin; @@ -94,7 +109,7 @@ abstract class _FiroCache { VACUUM; """, ); - usedTagsCacheDB.execute( + usedTagsCacheDB(network).execute( """ DELETE FROM SparkUsedCoinTags; VACUUM; diff --git a/lib/db/sqlite/firo_cache_coordinator.dart b/lib/db/sqlite/firo_cache_coordinator.dart index b2b39916a..fe720f804 100644 --- a/lib/db/sqlite/firo_cache_coordinator.dart +++ b/lib/db/sqlite/firo_cache_coordinator.dart @@ -5,7 +5,7 @@ typedef LTagPair = ({String tag, String txid}); /// Wrapper class for [_FiroCache] as [_FiroCache] should eventually be handled in a /// background isolate and [FiroCacheCoordinator] should manage that isolate abstract class FiroCacheCoordinator { - static _FiroCacheWorker? _worker; + static final Map _workers = {}; static bool _init = false; static Future init() async { @@ -14,20 +14,22 @@ abstract class FiroCacheCoordinator { } _init = true; await _FiroCache.init(); - _worker = await _FiroCacheWorker.spawn(); + for (final network in _FiroCache.networks) { + _workers[network] = await _FiroCacheWorker.spawn(network); + } } - static Future clearSharedCache() async { - return await _FiroCache._deleteAllCache(); + static Future clearSharedCache(CryptoCurrencyNetwork network) async { + return await _FiroCache._deleteAllCache(network); } - static Future getSparkCacheSize() async { + static Future getSparkCacheSize(CryptoCurrencyNetwork network) async { final dir = await StackFileSystem.applicationFiroCacheSQLiteDirectory(); final setCacheFile = File( - "${dir.path}/${_FiroCache.sparkSetCacheFileName}", + "${dir.path}/${_FiroCache.sparkSetCacheFileName(network)}", ); final usedTagsCacheFile = File( - "${dir.path}/${_FiroCache.sparkUsedTagsCacheFileName}", + "${dir.path}/${_FiroCache.sparkUsedTagsCacheFileName(network)}", ); final int bytes = ((await setCacheFile.exists()) ? await setCacheFile.length() : 0) + @@ -51,13 +53,14 @@ abstract class FiroCacheCoordinator { static Future runFetchAndUpdateSparkUsedCoinTags( ElectrumXClient client, + CryptoCurrencyNetwork network, ) async { - final count = await FiroCacheCoordinator.getUsedCoinTagsCount(); + final count = await FiroCacheCoordinator.getUsedCoinTagsCount(network); final unhashedTags = await client.getSparkUnhashedUsedCoinsTagsWithTxHashes( startNumber: count, ); if (unhashedTags.isNotEmpty) { - await _worker!.runTask( + await _workers[network]!.runTask( FCTask( func: FCFuncName._updateSparkUsedTagsWith, data: unhashedTags, @@ -69,10 +72,12 @@ abstract class FiroCacheCoordinator { static Future runFetchAndUpdateSparkAnonSetCacheForGroupId( int groupId, ElectrumXClient client, + CryptoCurrencyNetwork network, ) async { final blockhashResult = await FiroCacheCoordinator.getLatestSetInfoForGroupId( groupId, + network, ); final blockHash = blockhashResult?.blockHash ?? ""; @@ -81,7 +86,7 @@ abstract class FiroCacheCoordinator { startBlockHash: blockHash.toHexReversedFromBase64, ); - await _worker!.runTask( + await _workers[network]!.runTask( FCTask( func: FCFuncName._updateSparkAnonSetCoinsWith, data: (groupId, json), @@ -91,17 +96,22 @@ abstract class FiroCacheCoordinator { // =========================================================================== - static Future> getUsedCoinTags(int startNumber) async { + static Future> getUsedCoinTags( + int startNumber, + CryptoCurrencyNetwork network, + ) async { final result = await _Reader._getSparkUsedCoinTags( startNumber, - db: _FiroCache.usedTagsCacheDB, + db: _FiroCache.usedTagsCacheDB(network), ); return result.map((e) => e["tag"] as String).toSet(); } - static Future getUsedCoinTagsCount() async { + static Future getUsedCoinTagsCount( + CryptoCurrencyNetwork network, + ) async { final result = await _Reader._getUsedCoinTagsCount( - db: _FiroCache.usedTagsCacheDB, + db: _FiroCache.usedTagsCacheDB(network), ); if (result.isEmpty) { return 0; @@ -111,13 +121,14 @@ abstract class FiroCacheCoordinator { static Future> getUsedCoinTxidsFor({ required List tags, + required CryptoCurrencyNetwork network, }) async { if (tags.isEmpty) { return []; } final result = await _Reader._getUsedCoinTxidsFor( tags, - db: _FiroCache.usedTagsCacheDB, + db: _FiroCache.usedTagsCacheDB(network), ); if (result.isEmpty) { @@ -135,20 +146,22 @@ abstract class FiroCacheCoordinator { static Future> getUsedCoinTagsFor({ required String txid, + required CryptoCurrencyNetwork network, }) async { final result = await _Reader._getUsedCoinTagsFor( txid, - db: _FiroCache.usedTagsCacheDB, + db: _FiroCache.usedTagsCacheDB(network), ); return result.map((e) => e["tag"] as String).toSet(); } static Future checkTagIsUsed( String tag, + CryptoCurrencyNetwork network, ) async { return await _Reader._checkTagIsUsed( tag, - db: _FiroCache.usedTagsCacheDB, + db: _FiroCache.usedTagsCacheDB(network), ); } @@ -161,10 +174,11 @@ abstract class FiroCacheCoordinator { })>> getSetCoinsForGroupId( int groupId, { int? newerThanTimeStamp, + required CryptoCurrencyNetwork network, }) async { final resultSet = await _Reader._getSetCoinsForGroupId( groupId, - db: _FiroCache.setCacheDB, + db: _FiroCache.setCacheDB(network), newerThanTimeStamp: newerThanTimeStamp, ); return resultSet @@ -187,10 +201,11 @@ abstract class FiroCacheCoordinator { int timestampUTC, })?> getLatestSetInfoForGroupId( int groupId, + CryptoCurrencyNetwork network, ) async { final result = await _Reader._getLatestSetInfoForGroupId( groupId, - db: _FiroCache.setCacheDB, + db: _FiroCache.setCacheDB(network), ); if (result.isEmpty) { @@ -206,10 +221,11 @@ abstract class FiroCacheCoordinator { static Future checkSetInfoForGroupIdExists( int groupId, + CryptoCurrencyNetwork network, ) async { return await _Reader._checkSetInfoForGroupIdExists( groupId, - db: _FiroCache.setCacheDB, + db: _FiroCache.setCacheDB(network), ); } } diff --git a/lib/db/sqlite/firo_cache_worker.dart b/lib/db/sqlite/firo_cache_worker.dart index f6bfe68f1..71e407992 100644 --- a/lib/db/sqlite/firo_cache_worker.dart +++ b/lib/db/sqlite/firo_cache_worker.dart @@ -25,11 +25,12 @@ class _FiroCacheWorker { return await completer.future; } - static Future<_FiroCacheWorker> spawn() async { + static Future<_FiroCacheWorker> spawn(CryptoCurrencyNetwork network) async { final dir = await StackFileSystem.applicationFiroCacheSQLiteDirectory(); - final setCacheFilePath = "${dir.path}/${_FiroCache.sparkSetCacheFileName}"; + final setCacheFilePath = + "${dir.path}/${_FiroCache.sparkSetCacheFileName(network)}"; final usedTagsCacheFilePath = - "${dir.path}/${_FiroCache.sparkUsedTagsCacheFileName}"; + "${dir.path}/${_FiroCache.sparkUsedTagsCacheFileName(network)}"; final initPort = RawReceivePort(); final connection = Completer<(ReceivePort, SendPort)>.sync(); diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart index 004bd57df..098c1c285 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart @@ -430,7 +430,9 @@ class _WalletSettingsViewState extends ConsumerState { ), if (coin is Firo) FiroCacheCoordinator - .clearSharedCache(), + .clearSharedCache( + coin.network, + ), ], ), context: context, diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/spark_info.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/spark_info.dart index 7cb9b91f3..934736311 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/spark_info.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/spark_info.dart @@ -4,6 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../db/sqlite/firo_cache.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/text_styles.dart'; +import '../../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../../widgets/background.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../../widgets/detail_item.dart'; @@ -11,10 +12,13 @@ import '../../../../widgets/detail_item.dart'; class SparkInfoView extends ConsumerWidget { const SparkInfoView({ super.key, + required this.walletId, }); static const String routeName = "/sparkInfo"; + final String walletId; + @override Widget build(BuildContext context, WidgetRef ref) { return Background( @@ -37,7 +41,9 @@ class SparkInfoView extends ConsumerWidget { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ FutureBuilder( - future: FiroCacheCoordinator.getSparkCacheSize(), + future: FiroCacheCoordinator.getSparkCacheSize( + ref.watch(pWalletCoin(walletId)).network, + ), builder: (_, snapshot) { String detail = "Loading..."; if (snapshot.connectionState == ConnectionState.done) { diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart index d7e58ece5..37040ab71 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart @@ -243,6 +243,7 @@ class WalletSettingsWalletSettingsView extends ConsumerWidget { onPressed: () { Navigator.of(context).pushNamed( SparkInfoView.routeName, + arguments: walletId, ); }, child: Padding( diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart index cf47215d1..0105c0006 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart @@ -294,7 +294,9 @@ class _DesktopWalletViewState extends ConsumerState { width: 2, ), FutureBuilder( - future: FiroCacheCoordinator.getSparkCacheSize(), + future: FiroCacheCoordinator.getSparkCacheSize( + wallet.cryptoCurrency.network, + ), builder: (_, snapshot) => Text( snapshot.data ?? "", ), diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart index d440b9c4e..76fa097a4 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart @@ -19,8 +19,7 @@ import '../../../../../providers/global/wallets_provider.dart'; import '../../../../../themes/stack_colors.dart'; import '../../../../../utilities/assets.dart'; import '../../../../../utilities/text_styles.dart'; -import '../../../../../wallets/crypto_currency/coins/banano.dart'; -import '../../../../../wallets/crypto_currency/coins/firo.dart'; +import '../../../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../../../wallets/isar/models/wallet_info.dart'; import '../../../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../../../wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart'; @@ -187,7 +186,9 @@ class _MoreFeaturesDialogState extends ConsumerState { onPressed: () async => widget.onFusionPressed?.call(), ), if (wallet is SparkInterface) - const _MoreFeaturesClearSparkCacheItem(), + _MoreFeaturesClearSparkCacheItem( + cryptoCurrency: wallet.cryptoCurrency, + ), if (wallet is LelantusInterface) _MoreFeaturesItemBase( child: Row( @@ -371,10 +372,10 @@ class _MoreFeaturesItemBase extends StatelessWidget { class _MoreFeaturesClearSparkCacheItem extends StatefulWidget { const _MoreFeaturesClearSparkCacheItem({ super.key, + required this.cryptoCurrency, }); - static const double iconSizeBG = 46; - static const double iconSize = 24; + final CryptoCurrency cryptoCurrency; @override State<_MoreFeaturesClearSparkCacheItem> createState() => @@ -396,7 +397,9 @@ class _MoreFeaturesClearSparkCacheItemState } _onPressedLock = true; try { - await FiroCacheCoordinator.clearSharedCache(); + await FiroCacheCoordinator.clearSharedCache( + widget.cryptoCurrency.network, + ); setState(() { // trigger rebuild for cache size display }); @@ -434,7 +437,9 @@ class _MoreFeaturesClearSparkCacheItemState style: STextStyles.w600_20(context), ), FutureBuilder( - future: FiroCacheCoordinator.getSparkCacheSize(), + future: FiroCacheCoordinator.getSparkCacheSize( + widget.cryptoCurrency.network, + ), builder: (_, snapshot) { return Text( snapshot.data ?? "", diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 593d9e511..be011c90f 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -1982,13 +1982,18 @@ class RouteGenerator { return _routeError("${settings.name} invalid args: ${args.toString()}"); case SparkInfoView.routeName: - return getRoute( - shouldUseMaterialRoute: useMaterialPageRoute, - builder: (_) => const SparkInfoView(), - settings: RouteSettings( - name: settings.name, - ), - ); + if (args is String) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => SparkInfoView( + walletId: args, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); // == Desktop specific routes ============================================ case CreatePasswordView.routeName: diff --git a/lib/wallets/wallet/impl/firo_wallet.dart b/lib/wallets/wallet/impl/firo_wallet.dart index 9d77396ee..61c9346f7 100644 --- a/lib/wallets/wallet/impl/firo_wallet.dart +++ b/lib/wallets/wallet/impl/firo_wallet.dart @@ -387,6 +387,7 @@ class FiroWallet extends Bip39HDWallet parseAnonFees(); final tags = await FiroCacheCoordinator.getUsedCoinTagsFor( txid: txData["txid"] as String, + network: cryptoCurrency.network, ); spentSparkCoins = sparkCoinsInvolvedSpent .where( @@ -712,12 +713,14 @@ class FiroWallet extends Bip39HDWallet FiroCacheCoordinator.runFetchAndUpdateSparkAnonSetCacheForGroupId( i, electrumXClient, + cryptoCurrency.network, ), ); } final sparkUsedCoinTagsFuture = FiroCacheCoordinator.runFetchAndUpdateSparkUsedCoinTags( electrumXClient, + cryptoCurrency.network, ); // receiving addresses diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart index 8ebebc7ca..09d4dc6f1 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart @@ -278,13 +278,17 @@ mixin SparkInterface final List> setMaps = []; final List<({int groupId, String blockHash})> idAndBlockHashes = []; for (int i = 1; i <= currentId; i++) { - final resultSet = await FiroCacheCoordinator.getSetCoinsForGroupId(i); + final resultSet = await FiroCacheCoordinator.getSetCoinsForGroupId( + i, + network: cryptoCurrency.network, + ); if (resultSet.isEmpty) { continue; } final info = await FiroCacheCoordinator.getLatestSetInfoForGroupId( i, + cryptoCurrency.network, ); if (info == null) { throw Exception("The `info` should never be null here"); @@ -741,6 +745,7 @@ mixin SparkInterface final setExists = await FiroCacheCoordinator.checkSetInfoForGroupIdExists( id, + cryptoCurrency.network, ); if (!setExists) { groupIds.add(id); @@ -755,6 +760,7 @@ mixin SparkInterface FiroCacheCoordinator.runFetchAndUpdateSparkAnonSetCacheForGroupId( e, electrumXClient, + cryptoCurrency.network, ), ); @@ -763,6 +769,7 @@ mixin SparkInterface ...possibleFutures, FiroCacheCoordinator.runFetchAndUpdateSparkUsedCoinTags( electrumXClient, + cryptoCurrency.network, ), ]); @@ -782,11 +789,13 @@ mixin SparkInterface groupIdTimestampUTCMap[i.toString()] as int? ?? 0; final info = await FiroCacheCoordinator.getLatestSetInfoForGroupId( i, + cryptoCurrency.network, ); final anonymitySetResult = await FiroCacheCoordinator.getSetCoinsForGroupId( i, newerThanTimeStamp: lastCheckedTimeStampUTC, + network: cryptoCurrency.network, ); final coinsRaw = anonymitySetResult .map( @@ -882,7 +891,10 @@ mixin SparkInterface // only fetch tags from db if we need them to compare against any items // in coinsToCheck if (coinsToCheck.isNotEmpty) { - spentCoinTags = await FiroCacheCoordinator.getUsedCoinTags(0); + spentCoinTags = await FiroCacheCoordinator.getUsedCoinTags( + 0, + cryptoCurrency.network, + ); } // check and update coins if required @@ -992,6 +1004,7 @@ mixin SparkInterface final pairs = await FiroCacheCoordinator.getUsedCoinTxidsFor( tags: tags, + network: cryptoCurrency.network, ); pairs.removeWhere((e) => usedCoinTxidsFoundLocally.contains(e.txid));