Enhance background sync and wallet initialization across multiple wallet types

- Improve error handling and status checks during background sync
- Modify zano init method to not conflict with default init()
- Add dev tools for background sync monitoring and manual rescan
This commit is contained in:
Czarek Nakamoto 2025-02-20 17:47:27 +01:00
parent 6c046662a9
commit fcc2769597
10 changed files with 266 additions and 23 deletions

View file

@ -137,7 +137,7 @@ abstract class HavenWalletBase
} }
@override @override
Future<void> startSync() async { Future<void> startSync({bool isBackgroundSync = false}) async {
try { try {
_setInitialHeight(); _setInitialHeight();
} catch (_) {} } catch (_) {}

View file

@ -37,7 +37,7 @@ List<monero.SubaddressAccountRow> getAllAccount() {
int size = monero.SubaddressAccount_getAll_size(subaddressAccount!); int size = monero.SubaddressAccount_getAll_size(subaddressAccount!);
if (size == 0) { if (size == 0) {
monero.Wallet_addSubaddressAccount(wptr!); monero.Wallet_addSubaddressAccount(wptr!);
return getAllAccount(); return [];
} }
return List.generate(size, (index) { return List.generate(size, (index) {
return monero.SubaddressAccount_getAll_byIndex(subaddressAccount!, index: index); return monero.SubaddressAccount_getAll_byIndex(subaddressAccount!, index: index);

View file

@ -106,10 +106,9 @@ String getAddress({int accountIndex = 0, int addressIndex = 0}) {
int count = 0; int count = 0;
while (monero.Wallet_numSubaddresses(wptr!, accountIndex: accountIndex) - 1 < addressIndex) { while (monero.Wallet_numSubaddresses(wptr!, accountIndex: accountIndex) - 1 < addressIndex) {
printV("adding subaddress");
monero.Wallet_addSubaddress(wptr!, accountIndex: accountIndex); monero.Wallet_addSubaddress(wptr!, accountIndex: accountIndex);
if (count > 50) { if (count > 10) {
throw Exception("Failed to add subaddress"); break;
} }
count++; count++;
} }
@ -198,15 +197,6 @@ void setupBackgroundSync(
backgroundCachePassword: backgroundCachePassword); backgroundCachePassword: backgroundCachePassword);
} }
bool isBackgroundSyncing() => monero.Wallet_isBackgroundSyncing(wptr!);
void startBackgroundSync() {
monero.Wallet_startBackgroundSync(wptr!);
}
void stopBackgroundSync(String walletPassword) {
monero.Wallet_stopBackgroundSync(wptr!, walletPassword);
}
void stopSync() { void stopSync() {
monero.Wallet_init(wptr!, daemonAddress: ""); monero.Wallet_init(wptr!, daemonAddress: "");

View file

@ -232,10 +232,22 @@ abstract class MoneroWalletBase
walletPassword: password, walletPassword: password,
backgroundCachePassword: "testing-cache-password", backgroundCachePassword: "testing-cache-password",
); );
monero_wallet.startBackgroundSync(); monero.Wallet_startBackgroundSync(wptr!);
final status = monero.Wallet_status(wptr!);
if (status != 0) {
final err = monero.Wallet_errorString(wptr!);
printV("unable to start background sync: $err");
throw Exception("unable to start background sync: $err");
}
isBackgroundSyncing = true; isBackgroundSyncing = true;
} else { } else {
monero_wallet.stopBackgroundSync(password); monero.Wallet_stopBackgroundSync(wptr!, password);
final status = monero.Wallet_status(wptr!);
if (status != 0) {
final err = monero.Wallet_errorString(wptr!);
printV("unable to stop background sync: $err");
throw Exception("unable to stop background sync: $err");
}
isBackgroundSyncing = false; isBackgroundSyncing = false;
} }
monero_wallet.startRefresh(); monero_wallet.startRefresh();
@ -291,14 +303,20 @@ abstract class MoneroWalletBase
@override @override
Future<void> stopSync({bool isBackgroundSync = false}) async { Future<void> stopSync({bool isBackgroundSync = false}) async {
syncStatus = NotConnectedSyncStatus();
_listener?.stop();
if (isBackgroundSync) { if (isBackgroundSync) {
print("Stopping background sync");
monero.Wallet_stopBackgroundSync(wptr!, password);
final status = monero.Wallet_status(wptr!);
if (status != 0) {
final err = monero.Wallet_errorString(wptr!);
printV("unable to stop background sync: $err");
throw Exception("unable to stop background sync: $err");
}
isBackgroundSyncing = false; isBackgroundSyncing = false;
monero_wallet.stopWallet();
monero_wallet.stopBackgroundSync(password);
return; return;
} }
syncStatus = NotConnectedSyncStatus();
_listener?.stop();
monero_wallet.stopSync(); monero_wallet.stopSync();
_autoSaveTimer?.cancel(); _autoSaveTimer?.cancel();
monero_wallet.closeCurrentWallet(); monero_wallet.closeCurrentWallet();

View file

@ -37,6 +37,7 @@ import 'package:cake_wallet/entities/wallet_edit_page_arguments.dart';
import 'package:cake_wallet/entities/wallet_manager.dart'; import 'package:cake_wallet/entities/wallet_manager.dart';
import 'package:cake_wallet/src/screens/buy/buy_sell_options_page.dart'; import 'package:cake_wallet/src/screens/buy/buy_sell_options_page.dart';
import 'package:cake_wallet/src/screens/buy/payment_method_options_page.dart'; import 'package:cake_wallet/src/screens/buy/payment_method_options_page.dart';
import 'package:cake_wallet/src/screens/dev/monero_background_sync.dart';
import 'package:cake_wallet/src/screens/receive/address_list_page.dart'; import 'package:cake_wallet/src/screens/receive/address_list_page.dart';
import 'package:cake_wallet/src/screens/seed/seed_verification/seed_verification_page.dart'; import 'package:cake_wallet/src/screens/seed/seed_verification/seed_verification_page.dart';
import 'package:cake_wallet/src/screens/send/transaction_success_info_page.dart'; import 'package:cake_wallet/src/screens/send/transaction_success_info_page.dart';
@ -45,6 +46,7 @@ import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart';
import 'package:cake_wallet/src/screens/settings/mweb_logs_page.dart'; import 'package:cake_wallet/src/screens/settings/mweb_logs_page.dart';
import 'package:cake_wallet/src/screens/settings/mweb_node_page.dart'; import 'package:cake_wallet/src/screens/settings/mweb_node_page.dart';
import 'package:cake_wallet/src/screens/welcome/welcome_page.dart'; import 'package:cake_wallet/src/screens/welcome/welcome_page.dart';
import 'package:cake_wallet/view_model/dev/monero_background_sync.dart';
import 'package:cake_wallet/view_model/link_view_model.dart'; import 'package:cake_wallet/view_model/link_view_model.dart';
import 'package:cake_wallet/tron/tron.dart'; import 'package:cake_wallet/tron/tron.dart';
import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart'; import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart';
@ -905,8 +907,9 @@ Future<void> setup({
getIt.registerFactory(() => WalletSeedViewModel(getIt.get<AppStore>().wallet!)); getIt.registerFactory(() => WalletSeedViewModel(getIt.get<AppStore>().wallet!));
getIt.registerFactory<SeedSettingsViewModel>( getIt.registerFactory(() => DevMoneroBackgroundSync(getIt.get<AppStore>().wallet!));
() => SeedSettingsViewModel(getIt.get<AppStore>(), getIt.get<SeedSettingsStore>()));
getIt.registerFactory<SeedSettingsViewModel>(() => SeedSettingsViewModel(getIt.get<AppStore>(), getIt.get<SeedSettingsStore>()));
getIt.registerFactoryParam<WalletSeedPage, bool, void>((bool isWalletCreated, _) => getIt.registerFactoryParam<WalletSeedPage, bool, void>((bool isWalletCreated, _) =>
WalletSeedPage(getIt.get<WalletSeedViewModel>(), isNewWalletCreated: isWalletCreated)); WalletSeedPage(getIt.get<WalletSeedViewModel>(), isNewWalletCreated: isWalletCreated));
@ -1416,7 +1419,9 @@ Future<void> setup({
getIt.registerFactory(() => SignViewModel(getIt.get<AppStore>().wallet!)); getIt.registerFactory(() => SignViewModel(getIt.get<AppStore>().wallet!));
getIt.registerFactory(() => SeedVerificationPage(getIt.get<WalletSeedViewModel>())); getIt.registerFactory(() => SeedVerificationPage(getIt.get<WalletSeedViewModel>()));
getIt.registerFactory(() => DevMoneroBackgroundSyncPage(getIt.get<DevMoneroBackgroundSync>()));
_isSetupFinished = true; _isSetupFinished = true;
} }

View file

@ -36,6 +36,7 @@ import 'package:cake_wallet/src/screens/dashboard/pages/address_page.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/nft_details_page.dart'; import 'package:cake_wallet/src/screens/dashboard/pages/nft_details_page.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart'; import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart';
import 'package:cake_wallet/src/screens/dashboard/sign_page.dart'; import 'package:cake_wallet/src/screens/dashboard/sign_page.dart';
import 'package:cake_wallet/src/screens/dev/monero_background_sync.dart';
import 'package:cake_wallet/src/screens/disclaimer/disclaimer_page.dart'; import 'package:cake_wallet/src/screens/disclaimer/disclaimer_page.dart';
import 'package:cake_wallet/src/screens/exchange/exchange_page.dart'; import 'package:cake_wallet/src/screens/exchange/exchange_page.dart';
import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart'; import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart';
@ -818,6 +819,11 @@ Route<dynamic> createRoute(RouteSettings settings) {
return CupertinoPageRoute<void>( return CupertinoPageRoute<void>(
fullscreenDialog: true, builder: (_) => getIt.get<BackgroundSyncPage>()); fullscreenDialog: true, builder: (_) => getIt.get<BackgroundSyncPage>());
case Routes.devMoneroBackgroundSync:
return MaterialPageRoute<void>(
builder: (_) => getIt.get<DevMoneroBackgroundSyncPage>(),
);
default: default:
return MaterialPageRoute<void>( return MaterialPageRoute<void>(
builder: (_) => Scaffold( builder: (_) => Scaffold(

View file

@ -119,4 +119,6 @@ class Routes {
static const walletGroupDescription = '/wallet_group_description'; static const walletGroupDescription = '/wallet_group_description';
static const walletGroupExistingSeedDescriptionPage = '/wallet_group_existing_seed_description_page'; static const walletGroupExistingSeedDescriptionPage = '/wallet_group_existing_seed_description_page';
static const walletSeedVerificationPage = '/wallet_seed_verification_page'; static const walletSeedVerificationPage = '/wallet_seed_verification_page';
static const devMoneroBackgroundSync = '/dev/monero_background_sync';
} }

View file

@ -0,0 +1,112 @@
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/view_model/dev/monero_background_sync.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class DevMoneroBackgroundSyncPage extends BasePage {
final DevMoneroBackgroundSync viewModel;
DevMoneroBackgroundSyncPage(this.viewModel);
@override
String? get title => "[dev] xmr background sync";
Widget _buildSingleCell(String title, String value) {
return Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
padding: const EdgeInsets.all(8),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(title, style: TextStyle(fontWeight: FontWeight.bold)),
Text(value, maxLines: 1, overflow: TextOverflow.ellipsis),
],
),
);
}
@override
Widget body(BuildContext context) {
return Observer(
builder: (_) {
return GridView.count(
padding: const EdgeInsets.all(16),
crossAxisCount: 2,
childAspectRatio: 25/9,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
children: [
_buildSingleCell('Height (local)', viewModel.localBlockHeight ?? ''),
_buildSingleCell('Height (node)', viewModel.nodeBlockHeight ?? ''),
_buildSingleCell('Time', viewModel.tick.toString()),
_buildSingleCell('Background Sync', viewModel.isBackgroundSyncing ? 'Enabled' : 'Disabled'),
_buildSingleCell('Public View Key', viewModel.publicViewKey ?? ''),
_buildSingleCell('Private View Key', viewModel.privateViewKey ?? ''),
_buildSingleCell('Public Spend Key', viewModel.publicSpendKey ?? ''),
_buildSingleCell('Private Spend Key', viewModel.privateSpendKey ?? ''),
_buildSingleCell('Primary Address', viewModel.primaryAddress ?? ''),
_buildSingleCell('Passphrase', viewModel.passphrase ?? ''),
_buildSingleCell('Seed', viewModel.seed ?? ''),
_buildSingleCell('Seed Legacy', viewModel.seedLegacy ?? ''),
_enableBackgroundSyncButton(),
_disableBackgroundSyncButton(),
_refreshButton(),
_manualRescanButton(),
],
);
},
);
}
PrimaryButton _enableBackgroundSyncButton() {
return PrimaryButton(
text: "Enable background sync",
color: Colors.purple,
textColor: Colors.white,
onPressed: () {
viewModel.startBackgroundSync();
},
);
}
PrimaryButton _disableBackgroundSyncButton() {
return PrimaryButton(
text: "Disable background sync",
color: Colors.purple,
textColor: Colors.white,
onPressed: () {
viewModel.stopBackgroundSync();
},
);
}
PrimaryButton _refreshButton() {
return PrimaryButton(
text: viewModel.refreshTimer == null ? "Enable refresh" : "Disable refresh",
color: Colors.purple,
textColor: Colors.white,
onPressed: () {
if (viewModel.refreshTimer == null) {
viewModel.startRefreshTimer();
} else {
viewModel.stopRefreshTimer();
}
},
);
}
PrimaryButton _manualRescanButton() {
return PrimaryButton(
text: "Manual rescan",
color: Colors.purple,
textColor: Colors.white,
onPressed: () {
viewModel.manualRescan();
},
);
}
}

View file

@ -10,6 +10,7 @@ import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.
import 'package:cake_wallet/src/screens/settings/widgets/settings_version_cell.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_version_cell.dart';
import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart'; import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart';
@ -63,6 +64,12 @@ class OtherSettingsPage extends BasePage {
handler: (BuildContext context) => handler: (BuildContext context) =>
Navigator.of(context).pushNamed(Routes.readDisclaimer), Navigator.of(context).pushNamed(Routes.readDisclaimer),
), ),
if (kDebugMode && _otherSettingsViewModel.walletType == WalletType.monero)
SettingsCellWithArrow(
title: '[dev] monero background sync',
handler: (BuildContext context) =>
Navigator.of(context).pushNamed(Routes.devMoneroBackgroundSync),
),
Spacer(), Spacer(),
SettingsVersionCell( SettingsVersionCell(
title: S.of(context).version(_otherSettingsViewModel.currentVersion)), title: S.of(context).version(_otherSettingsViewModel.currentVersion)),

View file

@ -0,0 +1,103 @@
import 'dart:async';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cw_monero/monero_wallet.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_core/wallet_base.dart';
part 'monero_background_sync.g.dart';
class DevMoneroBackgroundSync = DevMoneroBackgroundSyncBase with _$DevMoneroBackgroundSync;
abstract class DevMoneroBackgroundSyncBase with Store {
DevMoneroBackgroundSyncBase(WalletBase wallet) : wallet = wallet;
final WalletBase wallet;
@observable
Timer? refreshTimer;
@observable
String? localBlockHeight;
@observable
String? nodeBlockHeight;
@observable
String? primaryAddress;
@observable
String? publicViewKey;
@observable
String? privateViewKey;
@observable
String? publicSpendKey;
@observable
String? privateSpendKey;
@observable
String? passphrase;
@observable
String? seed;
@observable
String? seedLegacy;
@observable
int tick = -1;
@observable
bool isBackgroundSyncing = false;
Future<void> _setValues() async {
final w = (wallet as MoneroWallet);
localBlockHeight = (await monero!.getCurrentHeight()).toString();
nodeBlockHeight = (await w.getNodeHeight()).toString();
final keys = w.keys;
primaryAddress = keys.primaryAddress;
publicViewKey = keys.publicViewKey;
privateViewKey = keys.privateViewKey;
publicSpendKey = keys.publicSpendKey;
privateSpendKey = keys.privateSpendKey;
passphrase = keys.passphrase;
seed = w.seed;
seedLegacy = w.seedLegacy("English");
tick = refreshTimer?.tick ?? -1;
isBackgroundSyncing = w.isBackgroundSyncing;
}
@action
void manualRescan() async {
final w = (wallet as MoneroWallet);
wallet.rescan(height: await w.getNodeHeight() - 10000);
}
@action
void startRefreshTimer() {
refreshTimer = Timer.periodic(Duration(seconds: 1), (timer) async {
await _setValues();
});
}
@action
void stopRefreshTimer() {
refreshTimer?.cancel();
refreshTimer = null;
}
@action
void startBackgroundSync() {
final w = (wallet as MoneroWallet);
w.startSync(isBackgroundSync: true);
}
@action
void stopBackgroundSync() {
final w = (wallet as MoneroWallet);
w.stopSync(isBackgroundSync: true);
}
}