Skip to content

Commit

Permalink
Disable autoreceive feature (#47)
Browse files Browse the repository at this point in the history
  • Loading branch information
KingGorrin committed Feb 26, 2024
1 parent 4b1e18a commit 0ae0f31
Show file tree
Hide file tree
Showing 16 changed files with 904 additions and 38 deletions.
74 changes: 56 additions & 18 deletions lib/blocs/auto_receive_tx_worker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:zenon_syrius_wallet_flutter/main.dart';
import 'package:zenon_syrius_wallet_flutter/model/model.dart';
import 'package:zenon_syrius_wallet_flutter/utils/account_block_utils.dart';
import 'package:zenon_syrius_wallet_flutter/utils/address_utils.dart';
import 'package:zenon_syrius_wallet_flutter/utils/constants.dart';
import 'package:znn_sdk_dart/znn_sdk_dart.dart';

class AutoReceiveTxWorker extends BaseBloc<WalletNotification> {
Expand All @@ -21,7 +22,41 @@ class AutoReceiveTxWorker extends BaseBloc<WalletNotification> {
return _instance!;
}

Future<void> autoReceiveTransactionHash(Hash currentHash) async {
if (!running) {
running = true;
try {
Address toAddress =
(await zenon!.ledger.getAccountBlockByHash(currentHash))!
.toAddress;
AccountBlockTemplate transactionParams = AccountBlockTemplate.receive(
currentHash,
);
AccountBlockTemplate response =
await AccountBlockUtils.createAccountBlock(
transactionParams,
'receive transaction',
address: toAddress,
waitForRequiredPlasma: true,
);
_sendSuccessNotification(response, toAddress.toString());
} on RpcException catch (e, stackTrace) {
_sendErrorNotification(e.toString());
Logger('AutoReceiveTxWorker')
.log(Level.WARNING, 'autoReceive', e, stackTrace);
}
running = false;
}
}

Future<void> autoReceive() async {
if (!sharedPrefsService!.get(
kAutoReceiveKey,
defaultValue: kAutoReceiveDefaultValue,
)) {
pool.clear();
return;
}
// Make sure that AutoUnlockHtlcWorker is not running since it should be
// given priority to send transactions.
if (pool.isNotEmpty && !running && !sl<AutoUnlockHtlcWorker>().running) {
Expand All @@ -41,15 +76,21 @@ class AutoReceiveTxWorker extends BaseBloc<WalletNotification> {
address: toAddress,
waitForRequiredPlasma: true,
);
pool.removeFirst();
_sendSuccessNotification(response, toAddress.toString());
if (pool.isNotEmpty) {
pool.removeFirst();
}
running = false;
autoReceive();
} on RpcException catch (e, stackTrace) {
_sendErrorNotification(e.toString());
Logger('AutoReceiveTxWorker')
.log(Level.WARNING, 'autoReceive', e, stackTrace);
if (e.message.compareTo('account-block from-block already received') ==
0) {
pool.removeFirst();
if (pool.isNotEmpty) {
pool.removeFirst();
}
} else {
_sendErrorNotification(e.toString());
}
Expand All @@ -60,6 +101,19 @@ class AutoReceiveTxWorker extends BaseBloc<WalletNotification> {
}
running = false;
}
return;
}

Future<void> addHash(Hash hash) async {
zenon!.stats.syncInfo().then((syncInfo) {
if (!pool.contains(hash) &&
(syncInfo.state == SyncState.syncDone ||
(syncInfo.targetHeight > 0 &&
syncInfo.currentHeight > 0 &&
(syncInfo.targetHeight - syncInfo.currentHeight) < 3))) {
pool.add(hash);
}
});
}

void _sendErrorNotification(String errorText) {
Expand All @@ -84,20 +138,4 @@ class AutoReceiveTxWorker extends BaseBloc<WalletNotification> {
),
);
}

Future<void> addHash(Hash hash) async {
if (!pool.contains(hash)) {
zenon!.stats.syncInfo().then((syncInfo) {
// Verify that the pool does not already contain the hash after the
// asynchronous request has completed and that the node is in sync.
if (!pool.contains(hash) &&
(syncInfo.state == SyncState.syncDone ||
(syncInfo.targetHeight > 0 &&
syncInfo.currentHeight > 0 &&
(syncInfo.targetHeight - syncInfo.currentHeight) < 3))) {
pool.add(hash);
}
});
}
}
}
3 changes: 0 additions & 3 deletions lib/blocs/node_sync_status_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ class NodeSyncStatusBloc extends DashboardBaseBloc<SyncInfo> {
(syncInfo.targetHeight - syncInfo.currentHeight) > 3))) {
lastSyncState = syncInfo.state;
if (syncInfo.state == SyncState.syncDone) {
NodeUtils.getUnreceivedTransactions().then((value) {
sl<AutoReceiveTxWorker>().autoReceive();
});
Future.delayed(const Duration(seconds: 5)).then((value) {
NodeUtils.getUnreceivedTransactions().then((value) {
sl<AutoReceiveTxWorker>().autoReceive();
Expand Down
17 changes: 17 additions & 0 deletions lib/blocs/transfer/pending_transactions_bloc.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import 'dart:async';

import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart';
import 'package:zenon_syrius_wallet_flutter/main.dart';
import 'package:zenon_syrius_wallet_flutter/utils/global.dart';
import 'package:znn_sdk_dart/znn_sdk_dart.dart';

class PendingTransactionsBloc extends InfiniteScrollBloc<AccountBlock> {
@override
Future<List<AccountBlock>> getData(int pageKey, int pageSize) async =>
(await zenon!.ledger.getUnreceivedBlocksByAddress(
Address.parse(kSelectedAddress!),
pageIndex: pageKey,
pageSize: pageSize,
))
.list!;
}
20 changes: 20 additions & 0 deletions lib/blocs/transfer/receive_transaction_bloc.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import 'package:flutter/material.dart';
import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart';
import 'package:zenon_syrius_wallet_flutter/main.dart';
import 'package:znn_sdk_dart/znn_sdk_dart.dart';

class ReceiveTransactionBloc extends BaseBloc<AccountBlockTemplate?> {
void receiveTransaction(String id, BuildContext context) {
try {
addEvent(null);
sl<AutoReceiveTxWorker>().autoReceiveTransactionHash(Hash.parse(id))
.onError(
(error, stackTrace) {
addError(error, stackTrace);
},
);
} catch (e, stackTrace) {
addError(e, stackTrace);
}
}
}
3 changes: 3 additions & 0 deletions lib/utils/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,12 @@ const String kAutoEraseNumAttemptsKey = 'auto_erase_num_attempts';
const String kLaunchAtStartupKey = 'launch_at_startup';
const String kEnableDesktopNotificationsKey = 'enable_desktop_notifications';
const String kEnableClipboardWatcherKey = 'enable_clipboard_watcher';
const String kAutoReceiveKey = 'enable_auto_receive';

const bool kLaunchAtStartupDefaultValue = false;
const bool kEnableDesktopNotificationsDefaultValue = false;
const bool kEnableClipboardWatcherDefaultValue = false;
const bool kAutoReceiveDefaultValue = true;

/// Node management
const String kChainIdKey = 'chain_id';
Expand Down
20 changes: 20 additions & 0 deletions lib/utils/format_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,24 @@ class FormatUtils {
)
.millisecondsSinceEpoch;
}

static String formatData(int transactionMillis) {
int currentMillis = DateTime.now().millisecondsSinceEpoch;
if (currentMillis - transactionMillis <=
const Duration(days: 1).inMilliseconds) {
return formatDataShort(currentMillis - transactionMillis);
}
return FormatUtils.formatDate(transactionMillis, dateFormat: 'MM/dd/yyyy');
}

static String formatDataShort(int i) {
Duration duration = Duration(milliseconds: i);
if (duration.inHours > 0) {
return '${duration.inHours} h ago';
}
if (duration.inMinutes > 0) {
return '${duration.inMinutes} min ago';
}
return '${duration.inSeconds} s ago';
}
}
13 changes: 11 additions & 2 deletions lib/utils/node_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ class NodeUtils {
await _getSubscriptionForMomentums();
await _getSubscriptionForAllAccountEvents();
await getUnreceivedTransactions();

sl<AutoReceiveTxWorker>().autoReceive();
Future.delayed(const Duration(seconds: 30))
.then((value) => NotificationUtils.sendNodeSyncingNotification());
Expand Down Expand Up @@ -127,7 +128,12 @@ class NodeUtils {

if (unreceivedBlocks.isNotEmpty) {
for (AccountBlock unreceivedBlock in unreceivedBlocks) {
sl<AutoReceiveTxWorker>().addHash(unreceivedBlock.hash);
if (sharedPrefsService!.get(
kAutoReceiveKey,
defaultValue: kAutoReceiveDefaultValue,
)) {
sl<AutoReceiveTxWorker>().addHash(unreceivedBlock.hash);
}
}
}
}
Expand Down Expand Up @@ -161,8 +167,11 @@ class NodeUtils {
static void _initListenForUnreceivedAccountBlocks(Stream broadcaster) {
broadcaster.listen(
(event) {
// Only process unreceived account blocks when autoReceive is enabled
if (event!.containsKey('method') &&
event['method'] == 'ledger.subscription') {
event['method'] == 'ledger.subscription' &&
sharedPrefsService!
.get(kAutoReceiveKey, defaultValue: kAutoReceiveDefaultValue)) {
for (var i = 0; i < event['params']['result'].length; i += 1) {
var tx = event['params']['result'][i];
if (tx.containsKey('toAddress') &&
Expand Down
83 changes: 76 additions & 7 deletions lib/widgets/modular_widgets/settings_widgets/wallet_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,17 @@ import 'dart:io';

import 'package:flutter/material.dart';
import 'package:launch_at_startup/launch_at_startup.dart';
import 'package:logging/logging.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart';
import 'package:zenon_syrius_wallet_flutter/main.dart';
import 'package:zenon_syrius_wallet_flutter/model/model.dart';
import 'package:zenon_syrius_wallet_flutter/screens/screens.dart';
import 'package:zenon_syrius_wallet_flutter/utils/clipboard_utils.dart';
import 'package:zenon_syrius_wallet_flutter/utils/constants.dart';
import 'package:zenon_syrius_wallet_flutter/utils/navigation_utils.dart';
import 'package:zenon_syrius_wallet_flutter/utils/notification_utils.dart';
import 'package:zenon_syrius_wallet_flutter/utils/utils.dart';
import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart';

class WalletOptions extends StatefulWidget {
final VoidCallback onResyncWalletPressed;

const WalletOptions(this.onResyncWalletPressed, {Key? key}) : super(key: key);
const WalletOptions({Key? key}) : super(key: key);

@override
State<WalletOptions> createState() => _WalletOptionsState();
Expand All @@ -26,6 +22,7 @@ class _WalletOptionsState extends State<WalletOptions> {
bool? _launchAtStartup;
bool? _enableDesktopNotifications;
bool? _enabledClipboardWatcher;
bool? _autoReceive;

@override
void initState() {
Expand All @@ -42,6 +39,10 @@ class _WalletOptionsState extends State<WalletOptions> {
kEnableClipboardWatcherKey,
defaultValue: kEnableClipboardWatcherDefaultValue,
);
_autoReceive = sharedPrefsService!.get(
kAutoReceiveKey,
defaultValue: kAutoReceiveDefaultValue,
);
}

@override
Expand Down Expand Up @@ -119,6 +120,7 @@ class _WalletOptionsState extends State<WalletOptions> {
_getLaunchAtStartupWidget(),
_getEnableDesktopNotifications(),
_buildEnableClipboardWatcher(),
_getAutoReceiveWidget()
],
);
}
Expand All @@ -144,6 +146,42 @@ class _WalletOptionsState extends State<WalletOptions> {
);
}

Widget _getAutoReceiveWidget() {
return Row(
children: [
Text(
'Auto-receiver',
style: Theme.of(context).textTheme.bodyMedium,
),
SyriusCheckbox(
onChanged: (value) async {
if (value == true) {
NodeUtils.getUnreceivedTransactions().then((value) {
sl<AutoReceiveTxWorker>().autoReceive();
}).onError((error, stackTrace) {
Logger('MainAppContainer').log(
Level.WARNING, '_getAutoReceiveWidget', error, stackTrace);
});
} else if (value == false &&
sl<AutoReceiveTxWorker>().pool.isNotEmpty) {
sl<AutoReceiveTxWorker>().pool.clear();
}
setState(() {
_autoReceive = value;
_changeAutoReceiveStatus(value ?? false);
});
},
value: _autoReceive,
context: context,
),
const StandardTooltipIcon(
'Disable the auto-receiver to protect against dusting attacks',
Icons.help,
),
],
);
}

Future<void> _setupLaunchAtStartup() async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
launchAtStartup.setup(
Expand All @@ -152,6 +190,37 @@ class _WalletOptionsState extends State<WalletOptions> {
);
}

Future<void> _changeAutoReceiveStatus(bool enabled) async {
try {
await _saveAutoReceiveValueToCache(enabled);
_sendAutoReceiveNotification(enabled);
} on Exception catch (e) {
NotificationUtils.sendNotificationError(
e,
'Something went wrong while setting automatic receive preference',
);
}
}

Future<void> _saveAutoReceiveValueToCache(bool enabled) async {
await sharedPrefsService!.put(
kAutoReceiveKey,
enabled,
);
}

void _sendAutoReceiveNotification(bool enabled) {
sl.get<NotificationsBloc>().addNotification(
WalletNotification(
title: 'Auto-receiver ${enabled ? 'enabled' : 'disabled'}',
details:
'Auto-receiver preference was ${enabled ? 'enabled' : 'disabled'}',
timestamp: DateTime.now().millisecondsSinceEpoch,
type: NotificationType.paymentSent,
),
);
}

Future<void> _changeLaunchAtStartupStatus(bool enabled) async {
try {
await _setupLaunchAtStartup();
Expand Down

0 comments on commit 0ae0f31

Please sign in to comment.