Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ZHTLC Resync on Application Resume #74

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/blocs/coins_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ class CoinsBloc implements BlocBase {

/// Handle the coins user has picked for activation.
/// Also used for coin activations during the application startup.
Future<void> enableCoins(List<Coin> coins) async {
Future<void> enableCoins(List<Coin> coins, {initialization = false}) async {
await pauseUntil(() => !_coinsLock, maxMs: 3000);
_coinsLock = true;

Expand Down Expand Up @@ -824,7 +824,7 @@ class CoinsBloc implements BlocBase {

if (transactions is Transactions) {
transactions.camouflageIfNeeded();
if (transactions.result.transactions.isNotEmpty) {
if ((transactions.result?.transactions ?? []).isNotEmpty) {
return transactions.result.transactions[0];
}
return null;
Expand Down
15 changes: 14 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import 'package:flutter/services.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_bloc/flutter_bloc.dart' as real_bloc;
import 'package:komodo_dex/packages/z_coin_activation/bloc/z_coin_activation_bloc.dart';
import 'package:komodo_dex/packages/z_coin_activation/bloc/z_coin_activation_event.dart';
import 'package:komodo_dex/packages/z_coin_activation/bloc/z_coin_activation_state.dart';
import 'package:komodo_dex/packages/z_coin_activation/bloc/z_coin_notifications.dart';
import 'package:komodo_dex/packages/z_coin_activation/widgets/z_coin_status_list_tile.dart';
import 'package:komodo_dex/services/mm.dart';
import '../app_config/app_config.dart';
import '../blocs/authenticate_bloc.dart';
import '../blocs/coins_bloc.dart';
Expand Down Expand Up @@ -178,6 +180,8 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
super.initState();
_initCheckNetworkStatus();
WidgetsBinding.instance.addObserver(this);

MM.untilRpcIsUp().then((_) => _requestResync());
}

@override
Expand Down Expand Up @@ -207,11 +211,20 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
Log('main', 'lifecycle: resumed');
mainBloc.isInBackground = false;
lockService.lockSignal(context);
await mmSe.handleWakeUp();
await mmSe.handleWakeUp().whenComplete(() {
if (mmSe.running) _requestResync();
});

break;
}
}

void _requestResync() {
context
.read<ZCoinActivationBloc>()
.add(ZCoinActivationRequested(resync: true));
}

@override
Widget build(BuildContext context) {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [
Expand Down
12 changes: 10 additions & 2 deletions lib/model/cex_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,10 @@ class CexPrices {
_init();
}

bool isInitialized = false;

Future<void> _init() async {
if (isInitialized) return;
prefs = await SharedPreferences.getInstance();
activeCurrency = prefs.getInt('activeCurrency') ?? 0;
_selectedFiat = prefs.getString('selectedFiat') ?? 'USD';
Expand All @@ -389,6 +392,8 @@ class CexPrices {
updatePrices();
updateRates();
});

isInitialized = true;
}

List<String> currencies;
Expand Down Expand Up @@ -617,7 +622,8 @@ class CexPrices {

Map<String, dynamic> json;
try {
json = jsonDecode(_body);
final isJsonString = _body.startsWith('{');
json = isJsonString ? jsonDecode(_body) : null;
} catch (e) {
Log('cex_provider', 'Failed to parse prices json: $e');
}
Expand Down Expand Up @@ -696,14 +702,16 @@ class CexPrices {

_fetchingPrices = false;

if (_body == null) return false;

Map<String, dynamic> json;
try {
json = jsonDecode(_body);
} catch (e) {
Log('cex_provider', 'Failed to parse prices json: $e');
}

if (json == null) return false;

if (json['error'] != null) {
Log('cex_provider', 'Prices endpoint error: ${json['error']}');
return false;
Expand Down
16 changes: 12 additions & 4 deletions lib/model/transaction_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,18 @@ class FeeDetails {
);

try {
// QRC20 tokens
feeDetails.totalFee = cutTrailingZeros(formatPrice(
double.parse(json['miner_fee']) +
double.parse(json['total_gas_fee'])));
final minerFee =
json['miner_fee'] == null ? null : double.tryParse(json['miner_fee']);

final totalGasFee = json['total_gas_fee'] == null
? null
: double.tryParse(json['total_gas_fee']);

if (minerFee != null || totalGasFee != null) {
final total = minerFee ?? 0.0 + totalGasFee ?? 0.0;
// QRC20 tokens
feeDetails.totalFee = cutTrailingZeros(formatPrice(total));
}
} catch (_) {}

return feeDetails;
Expand Down
26 changes: 14 additions & 12 deletions lib/model/transactions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,20 @@ class Result {
this.transactions,
});

factory Result.fromJson(Map<String, dynamic> json) => Result(
fromId: json['from_id'] ?? '',
limit: json['limit'] ?? 0,
skipped: json['skipped'] ?? 0,
total: json['total'] ?? 0,
currentBlock: json['current_block'] ?? 0,
syncStatus: json['sync_status'] == null
? SyncStatus()
: SyncStatus.fromJson(json['sync_status']),
transactions: List<Transaction>.from(
json['transactions'].map((dynamic x) => Transaction.fromJson(x))),
);
static Result fromJson(Map<String, dynamic> json) => json == null
? null
: Result(
fromId: json['from_id'] ?? '',
limit: json['limit'] ?? 0,
skipped: json['skipped'] ?? 0,
total: json['total'] ?? 0,
currentBlock: json['current_block'] ?? 0,
syncStatus: json['sync_status'] == null
? SyncStatus()
: SyncStatus.fromJson(json['sync_status']),
transactions: List<Transaction>.from(
json['transactions'].map((dynamic x) => Transaction.fromJson(x))),
);

String fromId;
int currentBlock;
Expand Down
20 changes: 5 additions & 15 deletions lib/packages/z_coin_activation/bloc/z_coin_activation_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -190,11 +190,11 @@ class ZCoinActivationApi {

Stream<ZCoinStatus> activateCoin(
String ticker, {
bool firstLaunch = false,
bool resync = false,
}) async* {
int coinTaskId = await getTaskId(ticker);
ZCoinStatus taskStatus;
if (!firstLaunch) {
if (!resync) {
final isAlreadyActivated = (await activatedZCoins()).contains(ticker);

taskStatus = coinTaskId == null
Expand Down Expand Up @@ -225,7 +225,7 @@ class ZCoinActivationApi {

ZCoinStatus lastEmittedStatus;

coinTaskId = await initiateActivation(ticker, noSyncParams: firstLaunch);
coinTaskId = await initiateActivation(ticker, noSyncParams: resync);

lastEmittedStatus = await activationTaskStatus(coinTaskId, ticker: ticker);

Expand Down Expand Up @@ -410,8 +410,8 @@ class ZCoinActivationApi {
Map<String, dynamic> responseBody,
String ticker,
}) async {
int _progress = 100;
String _messageDetails = '';
int _progress = 5;
String _messageDetails = 'Activating $ticker';
if (!responseBody.containsKey('result')) return null;

final result = responseBody['result'] is Map<String, dynamic>
Expand All @@ -420,13 +420,6 @@ class ZCoinActivationApi {
String status = result['status'];
dynamic details = result['details'];

// checkPointBlock will be removed
//Coin coin = coinsBloc.getKnownCoinByAbbr(ticker);
// int blockOffset = 0;
// if (coin.type == CoinType.zhtlc) {
// blockOffset = coin.protocol.protocolData.checkPointBlock?.height ?? 0;
// }

// use range from checkpoint block to present
if (status == 'Ok') {
if (details.containsKey('error')) {
Expand Down Expand Up @@ -487,9 +480,6 @@ class ZCoinActivationApi {
_messageDetails = isBuildingPhase
? 'Building $ticker wallet database'
: 'Updating $ticker blocks cache';
} else {
_progress = 5;
_messageDetails = 'Activating $ticker';
}

return ZCoinStatus(
Expand Down
38 changes: 21 additions & 17 deletions lib/packages/z_coin_activation/bloc/z_coin_activation_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,22 @@ class ZCoinActivationBloc
ZCoinActivationRequested event,
Emitter<ZCoinActivationState> emit,
) async {
final toActivate = await _repository.outstandingZCoinActivations();
final hasRequestedCoins =
(await _repository.getRequestedActivatedCoins()).isNotEmpty;

if (!hasRequestedCoins) {
emit(ZCoinActivationInitial());
return;
}

final isResync = event.resync;

final toActivate = isResync
? await _repository.getEnabledZCoins()
: await _repository.outstandingZCoinActivations();
final toActivateInitalCount = toActivate.length;

try {
final isAllCoinsEnabled = await _repository.isAllRequestedZCoinsEnabled();
if (isAllCoinsEnabled) {
add(ZCoinActivationStatusRequested());
return;
}

final zhtlcActivationPrefs = await loadZhtlcActivationPrefs();
SyncType zhtlcSyncType = zhtlcActivationPrefs['zhtlcSyncType'];

Expand All @@ -63,7 +69,9 @@ class ZCoinActivationBloc
),
);
await emit.forEach<ZCoinStatus>(
_repository.activateRequestedZCoins(),
event.resync
? _repository.resyncEnabledZCoins()
: _repository.activateRequestedZCoins(),
onData: (coinStatus) {
if (coinStatus.isFailed) {
return ZCoinActivationFailure(
Expand Down Expand Up @@ -108,7 +116,7 @@ class ZCoinActivationBloc
progress: shouldShowNewProgress ? overallProgress : lastProgress,
message: 'Activating ${coinStatus.coin}',
eta: eta,
startTime: previousInProgressState.startTime,
startTime: previousInProgressState?.startTime ?? DateTime.now(),
);
},
onError: (e, s) {
Expand Down Expand Up @@ -165,6 +173,8 @@ class ZCoinActivationBloc
try {
final isAllCoinsEnabled = await _repository.isAllRequestedZCoinsEnabled();

final mustResync = await isResyncing();

// TODO? Consider if better to base the "in progress" state on the
// API task status instead of the existing bloc state.
final isActivationInProgress = state is ZCoinActivationInProgess;
Expand All @@ -174,23 +184,17 @@ class ZCoinActivationBloc
}

ZCoinActivationState newState;
if (isAllCoinsEnabled) {
if (isAllCoinsEnabled && !mustResync) {
newState = isActivationInProgress
? ZCoinActivationSuccess()
: ZCoinActivationKnownState(isAllCoinsEnabled);
: ZCoinActivationKnownState(false);
} else if (isActivationInProgress) {
newState = state as ZCoinActivationInProgess;
} else {
newState = ZCoinActivationKnownState(isAllCoinsEnabled);
}

emit(newState);

if (!isAllCoinsEnabled && !isActivationInProgress) {
add(ZCoinActivationRequested());
} else {
_repository.willInitialize = false;
}
} catch (e) {
debugPrint('Failed to get activation status: $e');
emit(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ abstract class ZCoinActivationEvent {
}

/// Activates any requested ZCoins not already activated
class ZCoinActivationRequested extends ZCoinActivationEvent {}
class ZCoinActivationRequested extends ZCoinActivationEvent {
const ZCoinActivationRequested({this.resync = false});

final bool resync;
}

/// Sets the list of requested ZCoins to activate.
/// Must call [ZCoinActivationRequested] to activate the coins.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,23 @@ class ZCoinActivationRepository with RequestedZCoinsStorage {
static Future<String> get taskIdKey async =>
'activationTaskId_${(await Db.getCurrentWallet()).id}';

bool willInitialize = true;
Stream<ZCoinStatus> resyncEnabledZCoins() async* {
final enabledZCoins = await getEnabledZCoins();

Stream<ZCoinStatus> _activateZCoins(List<String> zCoins) async* {
try {
bool firstLaunch = willInitialize;
willInitialize = false;
yield* _activateZCoins(enabledZCoins, resyncOnly: true);
}

Stream<ZCoinStatus> _activateZCoins(
List<String> zCoins, {
bool resyncOnly = false,
}) async* {
try {
if (zCoins.isEmpty) return;

while (zCoins.isNotEmpty) {
final currentCoinTicker = zCoins.first;
await for (final update
in api.activateCoin(currentCoinTicker, firstLaunch: firstLaunch)) {
in api.activateCoin(currentCoinTicker, resync: resyncOnly)) {
Log(
'ZCoinActivationRepository:activateZCoins',
'Update received: ${update.toJson()}',
Expand Down Expand Up @@ -94,8 +98,6 @@ class ZCoinActivationRepository with RequestedZCoinsStorage {
Future<List<String>> outstandingZCoinActivations() async {
final requestedCoins = (await getRequestedActivatedCoins()).toSet();

if (willInitialize) return requestedCoins.toList();

final activatedZCoins = (await getEnabledZCoins()).toSet();

final coinsAlreadyActivated = activatedZCoins.intersection(requestedCoins);
Expand Down
6 changes: 4 additions & 2 deletions lib/packages/z_coin_activation/models/z_coin_status.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@ class ZCoinStatus {
final String message;
final double progress;

bool get isActivated => status == ActivationTaskStatus.active;
bool get isActivated =>
status == ActivationTaskStatus.active ||
message.contains('is activated already');

bool get isFailed => status == ActivationTaskStatus.failed;
bool get isFailed => status == ActivationTaskStatus.failed && !isActivated;

bool get isInProgress => status == ActivationTaskStatus.inProgress;

Expand Down
Loading
Loading