Skip to content

Commit

Permalink
Merge pull request #2151 from get10101/feat/full-sync-if-needed
Browse files Browse the repository at this point in the history
Perform full sync of app on-chain wallet on startup if needed
  • Loading branch information
luckysori authored Mar 5, 2024
2 parents a29c8e5 + 8a692fc commit ddff388
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 62 deletions.
2 changes: 1 addition & 1 deletion crates/commons/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mod rollover;
mod signature;
mod trade;

pub use crate::trade::*;
pub use backup::*;
pub use collab_revert::*;
pub use liquidity_option::*;
Expand All @@ -27,7 +28,6 @@ pub use price::Price;
pub use price::Prices;
pub use rollover::*;
pub use signature::*;
pub use trade::*;

pub const AUTH_SIGN_MESSAGE: &[u8; 19] = b"Hello it's me Mario";

Expand Down
14 changes: 14 additions & 0 deletions mobile/lib/common/domain/background_task.dart
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,17 @@ class CollabRevert {
return bridge.BackgroundTask_CollabRevert(TaskStatus.apiDummy());
}
}

class FullSync {
final TaskStatus taskStatus;

FullSync({required this.taskStatus});

static FullSync fromApi(bridge.BackgroundTask_FullSync fullSync) {
return FullSync(taskStatus: TaskStatus.fromApi(fullSync.field0));
}

static bridge.BackgroundTask apiDummy() {
return bridge.BackgroundTask_FullSync(TaskStatus.apiDummy());
}
}
57 changes: 57 additions & 0 deletions mobile/lib/common/full_sync_change_notifier.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import 'package:flutter/material.dart';
import 'package:get_10101/bridge_generated/bridge_definitions.dart' as bridge;
import 'package:get_10101/common/application/event_service.dart';
import 'package:get_10101/common/domain/background_task.dart';
import 'package:get_10101/common/global_keys.dart';
import 'package:get_10101/common/task_status_dialog.dart';
import 'package:get_10101/logger/logger.dart';
import 'package:provider/provider.dart';

class FullSyncChangeNotifier extends ChangeNotifier implements Subscriber {
late TaskStatus taskStatus;

@override
void notify(bridge.Event event) async {
if (event is bridge.Event_BackgroundNotification) {
if (event.field0 is! bridge.BackgroundTask_FullSync) {
// ignoring other kinds of background tasks
return;
}
FullSync fullSync = FullSync.fromApi(event.field0 as bridge.BackgroundTask_FullSync);
logger.d("Received a full sync event. Status: ${fullSync.taskStatus}");

taskStatus = fullSync.taskStatus;

if (taskStatus == TaskStatus.pending) {
while (shellNavigatorKey.currentContext == null) {
await Future.delayed(const Duration(milliseconds: 100)); // Adjust delay as needed
}

// initialize dialog for the pending task
showDialog(
context: shellNavigatorKey.currentContext!,
builder: (context) {
TaskStatus status = context.watch<FullSyncChangeNotifier>().taskStatus;

Widget content;
switch (status) {
case TaskStatus.pending:
content = const Text("Waiting for on-chain sync to complete");
case TaskStatus.success:
content = const Text(
"Full on-chain sync completed. If your balance is still incomplete, go to Wallet Settings to trigger further syncs.");
case TaskStatus.failed:
content = const Text(
"Full on-chain sync failed. You can keep trying by shutting down the app and restarting.");
}

return TaskStatusDialog(title: "Full wallet sync", status: status, content: content);
},
);
} else {
// notify dialog about changed task status
notifyListeners();
}
}
}
}
6 changes: 6 additions & 0 deletions mobile/lib/common/init_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:get_10101/common/application/lsp_change_notifier.dart';
import 'package:get_10101/common/dlc_channel_change_notifier.dart';
import 'package:get_10101/common/dlc_channel_service.dart';
import 'package:get_10101/common/domain/lsp_config.dart';
import 'package:get_10101/common/full_sync_change_notifier.dart';
import 'package:get_10101/features/brag/github_service.dart';
import 'package:get_10101/features/trade/candlestick_change_notifier.dart';
import 'package:get_10101/features/trade/order_change_notifier.dart';
Expand Down Expand Up @@ -66,6 +67,7 @@ List<SingleChildWidget> createProviders() {
ChangeNotifierProvider(create: (context) => CollabRevertChangeNotifier()),
ChangeNotifierProvider(create: (context) => LspChangeNotifier(channelInfoService)),
ChangeNotifierProvider(create: (context) => PollChangeNotifier(pollService)),
ChangeNotifierProvider(create: (context) => FullSyncChangeNotifier()),
Provider(create: (context) => config),
Provider(create: (context) => channelInfoService),
Provider(create: (context) => pollService),
Expand Down Expand Up @@ -93,6 +95,7 @@ void subscribeToNotifiers(BuildContext context) {
final recoverDlcChangeNotifier = context.read<RecoverDlcChangeNotifier>();
final collabRevertChangeNotifier = context.read<CollabRevertChangeNotifier>();
final lspConfigChangeNotifier = context.read<LspChangeNotifier>();
final fullSyncChangeNotifier = context.read<FullSyncChangeNotifier>();

eventService.subscribe(
orderChangeNotifier, bridge.Event.orderUpdateNotification(Order.apiDummy()));
Expand Down Expand Up @@ -136,6 +139,9 @@ void subscribeToNotifiers(BuildContext context) {

eventService.subscribe(lspConfigChangeNotifier, bridge.Event.authenticated(LspConfig.apiDummy()));

eventService.subscribe(
fullSyncChangeNotifier, bridge.Event.backgroundNotification(FullSync.apiDummy()));

eventService.subscribe(
AnonSubscriber((event) => logger.i(event.field0)), const bridge.Event.log(""));
}
125 changes: 66 additions & 59 deletions mobile/lib/common/settings/wallet_settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,69 +66,76 @@ class _WalletSettingsState extends State<WalletSettings> {
height: 20,
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"The amount of addresses to sync for (at least). Once you confirm, a full wallet sync will be performed. The higher the gap is, the longer the sync will take. Hence, we recommend syncing incrementally.",
style: TextStyle(fontSize: 18),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16),
child: Stack(
alignment: Alignment.centerRight,
children: [
TextFormField(
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
],
keyboardType: TextInputType.number,
controller: lookAheadController,
decoration: const InputDecoration(
border: UnderlineInputBorder(),
labelText: 'Wallet Gap',
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"Full sync \n",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const Text(
"Select the stop gap and confirm to perform a full wallet sync.\n\nThe stop gap determines how many consecutive unused addresses the wallet will have to find to stop syncing. The bigger the gap, the longer the sync will take. Hence, we recommend syncing incrementally.\n\nFor example: use a stop gap of 5, wait for the sync to complete and check your balance; if your balance still seems incorrect, come back here and use a stop gap of 10, etc.",
style: TextStyle(fontSize: 18),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16),
child: Stack(
alignment: Alignment.centerRight,
children: [
TextFormField(
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
],
keyboardType: TextInputType.number,
controller: lookAheadController,
decoration: const InputDecoration(
border: UnderlineInputBorder(),
labelText: 'Stop Gap',
),
),
),
Visibility(
visible: !syncing,
replacement: const CircularProgressIndicator(),
child: IconButton(
icon: const Icon(
Icons.check,
color: Colors.green,
),
onPressed: () async {
final messenger = ScaffoldMessenger.of(context);
try {
var gap = lookAheadController.value.text;
var gapAsNumber = int.parse(gap);
Visibility(
visible: !syncing,
replacement: const CircularProgressIndicator(),
child: IconButton(
icon: const Icon(
Icons.check,
color: Colors.green,
),
onPressed: () async {
final messenger = ScaffoldMessenger.of(context);
try {
var gap = lookAheadController.value.text;
var gapAsNumber = int.parse(gap);

setState(() {
syncing = true;
});
setState(() {
syncing = true;
});

await rust.api.fullSync(stopGap: gapAsNumber);
showSnackBar(messenger, "Successfully synced for new gap.");
await rust.api.fullSync(stopGap: gapAsNumber);
showSnackBar(messenger, "Successfully synced for new gap.");

setState(() {
syncing = false;
});
} catch (exception) {
logger.e("Failed to complete full sync $exception");
showSnackBar(
messenger, "Error when running full sync $exception");
} finally {
setState(() {
syncing = false;
});
}
},
))
],
),
)
],
setState(() {
syncing = false;
});
} catch (exception) {
logger.e("Failed to complete full sync $exception");
showSnackBar(
messenger, "Error when running full sync $exception");
} finally {
setState(() {
syncing = false;
});
}
},
))
],
),
)
],
),
),
),
],
Expand Down
3 changes: 3 additions & 0 deletions mobile/native/src/event/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ pub enum BackgroundTask {
RecoverDlc(TaskStatus),
/// The coordinator wants to collaboratively close a ln channel with a stuck position.
CollabRevert(TaskStatus),
/// The app is performing a full sync of the on-chain wallet.
FullSync(TaskStatus),
}

impl From<EventInternal> for Event {
Expand Down Expand Up @@ -141,6 +143,7 @@ impl From<event::BackgroundTask> for BackgroundTask {
event::BackgroundTask::CollabRevert(status) => {
BackgroundTask::CollabRevert(status.into())
}
event::BackgroundTask::FullSync(status) => BackgroundTask::FullSync(status.into()),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions mobile/native/src/event/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub enum BackgroundTask {
Rollover(TaskStatus),
CollabRevert(TaskStatus),
RecoverDlc(TaskStatus),
FullSync(TaskStatus),
}

#[derive(Clone, Debug)]
Expand Down
60 changes: 58 additions & 2 deletions mobile/native/src/ln_dlc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ const UPDATE_WALLET_HISTORY_INTERVAL: Duration = Duration::from_secs(5);
const CHECK_OPEN_ORDERS_INTERVAL: Duration = Duration::from_secs(60);
const ON_CHAIN_SYNC_INTERVAL: Duration = Duration::from_secs(300);

/// The name of the BDK wallet database file.
const WALLET_DB_FILE_NAME: &str = "bdk-wallet";

/// The prefix to the [`bdk_file_store`] database file where BDK persists
/// [`bdk::wallet::ChangeSet`]s.
///
Expand Down Expand Up @@ -310,7 +313,7 @@ pub fn run(seed_dir: String, runtime: &Runtime) -> Result<()> {
let node_event_handler = Arc::new(NodeEventHandler::new());

let wallet_storage = {
let wallet_dir = Path::new(&config::get_data_dir()).join("wallet");
let wallet_dir = Path::new(&config::get_data_dir()).join(WALLET_DB_FILE_NAME);
bdk_file_store::Store::open_or_create_new(WALLET_DB_PREFIX.as_bytes(), wallet_dir)?
};

Expand Down Expand Up @@ -426,14 +429,67 @@ pub fn run(seed_dir: String, runtime: &Runtime) -> Result<()> {
}
});

state::set_node(node);
state::set_node(node.clone());

event::publish(&EventInternal::Init("10101 is ready.".to_string()));

tokio::spawn(full_sync_on_wallet_db_migration());

Ok(())
})
}

pub async fn full_sync_on_wallet_db_migration() {
let node = state::get_node();

let old_wallet_db_path = Path::new(&config::get_data_dir()).join("wallet");
if old_wallet_db_path.exists() {
event::publish(&EventInternal::BackgroundNotification(
event::BackgroundTask::FullSync(event::TaskStatus::Pending),
));

let stop_gap = 20;
tracing::info!(
%stop_gap,
"Old wallet DB detected. Attempting to populate new wallet with full sync"
);

match node.inner.full_sync(stop_gap).await {
Ok(_) => {
tracing::info!("Full sync successful");

// Spawn into the blocking thread pool of the dedicated backend runtime to avoid
// blocking the UI thread.
if let Ok(runtime) = state::get_or_create_tokio_runtime() {
runtime
.spawn_blocking(move || {
if let Err(e) = keep_wallet_balance_and_history_up_to_date(&node) {
tracing::error!("Failed to keep wallet history up to date: {e:#}");
}
})
.await
.expect("task to complete");
}

event::publish(&EventInternal::BackgroundNotification(
event::BackgroundTask::FullSync(event::TaskStatus::Success),
));

if let Err(e) = std::fs::remove_file(old_wallet_db_path) {
tracing::info!("Failed to delete old wallet DB file: {e:#}");
}
}
Err(e) => {
tracing::error!("Full sync failed: {e:#}");

event::publish(&EventInternal::BackgroundNotification(
event::BackgroundTask::FullSync(event::TaskStatus::Failed),
));
}
};
}
}

pub fn init_new_mnemonic(target_seed_file: &Path) -> Result<()> {
let seed = Bip39Seed::initialize(target_seed_file)?;
state::set_seed(seed);
Expand Down

0 comments on commit ddff388

Please sign in to comment.