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
Future<void> startSync() async {
Future<void> startSync({bool isBackgroundSync = false}) async {
try {
_setInitialHeight();
} catch (_) {}

View file

@ -37,7 +37,7 @@ List<monero.SubaddressAccountRow> getAllAccount() {
int size = monero.SubaddressAccount_getAll_size(subaddressAccount!);
if (size == 0) {
monero.Wallet_addSubaddressAccount(wptr!);
return getAllAccount();
return [];
}
return List.generate(size, (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;
while (monero.Wallet_numSubaddresses(wptr!, accountIndex: accountIndex) - 1 < addressIndex) {
printV("adding subaddress");
monero.Wallet_addSubaddress(wptr!, accountIndex: accountIndex);
if (count > 50) {
throw Exception("Failed to add subaddress");
if (count > 10) {
break;
}
count++;
}
@ -198,15 +197,6 @@ void setupBackgroundSync(
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() {
monero.Wallet_init(wptr!, daemonAddress: "");

View file

@ -232,10 +232,22 @@ abstract class MoneroWalletBase
walletPassword: 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;
} 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;
}
monero_wallet.startRefresh();
@ -291,14 +303,20 @@ abstract class MoneroWalletBase
@override
Future<void> stopSync({bool isBackgroundSync = false}) async {
syncStatus = NotConnectedSyncStatus();
_listener?.stop();
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;
monero_wallet.stopWallet();
monero_wallet.stopBackgroundSync(password);
return;
}
syncStatus = NotConnectedSyncStatus();
_listener?.stop();
monero_wallet.stopSync();
_autoSaveTimer?.cancel();
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/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/dev/monero_background_sync.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/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_node_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/tron/tron.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<SeedSettingsViewModel>(
() => SeedSettingsViewModel(getIt.get<AppStore>(), getIt.get<SeedSettingsStore>()));
getIt.registerFactory(() => DevMoneroBackgroundSync(getIt.get<AppStore>().wallet!));
getIt.registerFactory<SeedSettingsViewModel>(() => SeedSettingsViewModel(getIt.get<AppStore>(), getIt.get<SeedSettingsStore>()));
getIt.registerFactoryParam<WalletSeedPage, bool, void>((bool isWalletCreated, _) =>
WalletSeedPage(getIt.get<WalletSeedViewModel>(), isNewWalletCreated: isWalletCreated));
@ -1416,7 +1419,9 @@ Future<void> setup({
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;
}

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/transactions_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/exchange/exchange_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>(
fullscreenDialog: true, builder: (_) => getIt.get<BackgroundSyncPage>());
case Routes.devMoneroBackgroundSync:
return MaterialPageRoute<void>(
builder: (_) => getIt.get<DevMoneroBackgroundSyncPage>(),
);
default:
return MaterialPageRoute<void>(
builder: (_) => Scaffold(

View file

@ -119,4 +119,6 @@ class Routes {
static const walletGroupDescription = '/wallet_group_description';
static const walletGroupExistingSeedDescriptionPage = '/wallet_group_existing_seed_description_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/view_model/settings/other_settings_view_model.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
@ -63,6 +64,12 @@ class OtherSettingsPage extends BasePage {
handler: (BuildContext context) =>
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(),
SettingsVersionCell(
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);
}
}