mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-12-22 19:39:22 +00:00
Merge pull request #1002 from cypherstack/firo-unstable-connection
unstable firo
This commit is contained in:
commit
a04ea494a9
10 changed files with 176 additions and 63 deletions
|
@ -27,6 +27,8 @@ abstract class AppConfig {
|
|||
|
||||
static List<CryptoCurrency> get coins => _supportedCoins;
|
||||
|
||||
static ({String from, String to}) get swapDefaults => _swapDefaults;
|
||||
|
||||
static bool get isSingleCoinApp => coins.length == 1;
|
||||
|
||||
static CryptoCurrency? getCryptoCurrencyFor(String coinIdentifier) {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:electrum_adapter/electrum_adapter.dart';
|
||||
|
||||
import '../utilities/logger.dart';
|
||||
import '../wallets/crypto_currency/crypto_currency.dart';
|
||||
|
||||
class ClientManager {
|
||||
|
@ -37,13 +39,19 @@ class ClientManager {
|
|||
}
|
||||
|
||||
_heightCompleters[key] = Completer<int>();
|
||||
_subscriptions[key] = client.subscribeHeaders().listen((event) {
|
||||
_heights[key] = event.height;
|
||||
_subscriptions[key] = client.subscribeHeaders().listen(
|
||||
(event) {
|
||||
_heights[key] = event.height;
|
||||
|
||||
if (!_heightCompleters[key]!.isCompleted) {
|
||||
_heightCompleters[key]!.complete(event.height);
|
||||
}
|
||||
});
|
||||
if (!_heightCompleters[key]!.isCompleted) {
|
||||
_heightCompleters[key]!.complete(event.height);
|
||||
}
|
||||
},
|
||||
onError: (Object err, StackTrace s) => Logging.instance.log(
|
||||
"ClientManager listen: $err\n$s",
|
||||
level: LogLevel.Error,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<int> getChainHeightFor(CryptoCurrency cryptoCurrency) async {
|
||||
|
|
|
@ -490,7 +490,15 @@ class ElectrumXClient {
|
|||
command: 'server.ping',
|
||||
requestTimeout: const Duration(seconds: 2),
|
||||
retries: retryCount,
|
||||
).timeout(const Duration(seconds: 2)) as bool;
|
||||
).timeout(
|
||||
const Duration(seconds: 2),
|
||||
onTimeout: () {
|
||||
Logging.instance.log(
|
||||
"ElectrumxClient.ping timed out with retryCount=$retryCount, host=$_host",
|
||||
level: LogLevel.Debug,
|
||||
);
|
||||
},
|
||||
) as bool;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'dart:io';
|
|||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:socks5_proxy/socks_client.dart';
|
||||
|
||||
import '../utilities/logger.dart';
|
||||
|
||||
// WIP wrapper layer
|
||||
|
@ -118,6 +119,10 @@ class HTTP {
|
|||
onDone: () => completer.complete(
|
||||
Uint8List.fromList(bytes),
|
||||
),
|
||||
onError: (Object err, StackTrace s) => Logging.instance.log(
|
||||
"Http wrapper layer listen: $err\n$s",
|
||||
level: LogLevel.Error,
|
||||
),
|
||||
);
|
||||
return completer.future;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import 'package:flutter/foundation.dart';
|
|||
import 'package:isar/isar.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import '../../app_config.dart';
|
||||
import '../../db/hive/db.dart';
|
||||
import '../../models/exchange/active_pair.dart';
|
||||
import '../../models/exchange/aggregate_currency.dart';
|
||||
|
@ -79,7 +80,7 @@ class ExchangeDataLoadingService {
|
|||
if (await isar.currencies.count() > 0) {
|
||||
pair?.setSend(
|
||||
await getAggregateCurrency(
|
||||
"BTC",
|
||||
AppConfig.swapDefaults.from,
|
||||
rateType,
|
||||
null,
|
||||
),
|
||||
|
@ -88,7 +89,7 @@ class ExchangeDataLoadingService {
|
|||
|
||||
pair?.setReceive(
|
||||
await getAggregateCurrency(
|
||||
"XMR",
|
||||
AppConfig.swapDefaults.to,
|
||||
rateType,
|
||||
null,
|
||||
),
|
||||
|
|
|
@ -411,6 +411,17 @@ abstract class Wallet<T extends CryptoCurrency> {
|
|||
);
|
||||
|
||||
_isConnected = hasNetwork;
|
||||
|
||||
if (status == NodeConnectionStatus.disconnected) {
|
||||
GlobalEventBus.instance.fire(
|
||||
WalletSyncStatusChangedEvent(
|
||||
WalletSyncStatus.unableToSync,
|
||||
walletId,
|
||||
cryptoCurrency,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (hasNetwork) {
|
||||
unawaited(refresh());
|
||||
}
|
||||
|
@ -476,6 +487,61 @@ abstract class Wallet<T extends CryptoCurrency> {
|
|||
|
||||
// Should fire events
|
||||
Future<void> refresh() async {
|
||||
final refreshCompleter = Completer<void>();
|
||||
final future = refreshCompleter.future.then(
|
||||
(_) {
|
||||
GlobalEventBus.instance.fire(
|
||||
WalletSyncStatusChangedEvent(
|
||||
WalletSyncStatus.synced,
|
||||
walletId,
|
||||
cryptoCurrency,
|
||||
),
|
||||
);
|
||||
|
||||
if (shouldAutoSync) {
|
||||
_periodicRefreshTimer ??=
|
||||
Timer.periodic(const Duration(seconds: 150), (timer) async {
|
||||
// chain height check currently broken
|
||||
// if ((await chainHeight) != (await storedChainHeight)) {
|
||||
|
||||
// TODO: [prio=med] some kind of quick check if wallet needs to refresh to replace the old refreshIfThereIsNewData call
|
||||
// if (await refreshIfThereIsNewData()) {
|
||||
unawaited(refresh());
|
||||
|
||||
// }
|
||||
// }
|
||||
});
|
||||
}
|
||||
},
|
||||
onError: (Object error, StackTrace strace) {
|
||||
GlobalEventBus.instance.fire(
|
||||
NodeConnectionStatusChangedEvent(
|
||||
NodeConnectionStatus.disconnected,
|
||||
walletId,
|
||||
cryptoCurrency,
|
||||
),
|
||||
);
|
||||
GlobalEventBus.instance.fire(
|
||||
WalletSyncStatusChangedEvent(
|
||||
WalletSyncStatus.unableToSync,
|
||||
walletId,
|
||||
cryptoCurrency,
|
||||
),
|
||||
);
|
||||
Logging.instance.log(
|
||||
"Caught exception in refreshWalletData(): $error\n$strace",
|
||||
level: LogLevel.Error,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
unawaited(_refresh(refreshCompleter));
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
// Should fire events
|
||||
Future<void> _refresh(Completer<void> completer) async {
|
||||
// Awaiting this lock could be dangerous.
|
||||
// Since refresh is periodic (generally)
|
||||
if (refreshMutex.isLocked) {
|
||||
|
@ -483,6 +549,22 @@ abstract class Wallet<T extends CryptoCurrency> {
|
|||
}
|
||||
final start = DateTime.now();
|
||||
|
||||
bool tAlive = true;
|
||||
final t = Timer.periodic(const Duration(seconds: 1), (timer) async {
|
||||
if (tAlive) {
|
||||
final pingSuccess = await pingCheck();
|
||||
if (!pingSuccess) {
|
||||
tAlive = false;
|
||||
}
|
||||
} else {
|
||||
timer.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
void _checkAlive() {
|
||||
if (!tAlive) throw Exception("refresh alive ping failure");
|
||||
}
|
||||
|
||||
try {
|
||||
// this acquire should be almost instant due to above check.
|
||||
// Slight possibility of race but should be irrelevant
|
||||
|
@ -496,136 +578,135 @@ abstract class Wallet<T extends CryptoCurrency> {
|
|||
),
|
||||
);
|
||||
|
||||
_checkAlive();
|
||||
|
||||
// add some small buffer before making calls.
|
||||
// this can probably be removed in the future but was added as a
|
||||
// debugging feature
|
||||
await Future<void>.delayed(const Duration(milliseconds: 300));
|
||||
_checkAlive();
|
||||
|
||||
// TODO: [prio=low] handle this differently. Extra modification of this file for coin specific functionality should be avoided.
|
||||
final Set<String> codesToCheck = {};
|
||||
_checkAlive();
|
||||
if (this is PaynymInterface) {
|
||||
// isSegwit does not matter here at all
|
||||
final myCode =
|
||||
await (this as PaynymInterface).getPaymentCode(isSegwit: false);
|
||||
_checkAlive();
|
||||
|
||||
final nym = await PaynymIsApi().nym(myCode.toString());
|
||||
_checkAlive();
|
||||
if (nym.value != null) {
|
||||
for (final follower in nym.value!.followers) {
|
||||
codesToCheck.add(follower.code);
|
||||
}
|
||||
_checkAlive();
|
||||
for (final following in nym.value!.following) {
|
||||
codesToCheck.add(following.code);
|
||||
}
|
||||
}
|
||||
_checkAlive();
|
||||
}
|
||||
|
||||
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.0, walletId));
|
||||
_checkAlive();
|
||||
await updateChainHeight();
|
||||
|
||||
_checkAlive();
|
||||
if (this is BitcoinFrostWallet) {
|
||||
await (this as BitcoinFrostWallet).lookAhead();
|
||||
}
|
||||
_checkAlive();
|
||||
|
||||
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.1, walletId));
|
||||
|
||||
_checkAlive();
|
||||
// TODO: [prio=low] handle this differently. Extra modification of this file for coin specific functionality should be avoided.
|
||||
if (this is MultiAddressInterface) {
|
||||
if (info.otherData[WalletInfoKeys.reuseAddress] != true) {
|
||||
await (this as MultiAddressInterface)
|
||||
.checkReceivingAddressForTransactions();
|
||||
}
|
||||
_checkAlive();
|
||||
}
|
||||
|
||||
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.2, walletId));
|
||||
_checkAlive();
|
||||
|
||||
// TODO: [prio=low] handle this differently. Extra modification of this file for coin specific functionality should be avoided.
|
||||
if (this is MultiAddressInterface) {
|
||||
await (this as MultiAddressInterface)
|
||||
.checkChangeAddressForTransactions();
|
||||
}
|
||||
_checkAlive();
|
||||
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.3, walletId));
|
||||
if (this is SparkInterface) {
|
||||
// this should be called before updateTransactions()
|
||||
await (this as SparkInterface).refreshSparkData();
|
||||
}
|
||||
_checkAlive();
|
||||
|
||||
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.50, walletId));
|
||||
_checkAlive();
|
||||
final fetchFuture = updateTransactions();
|
||||
_checkAlive();
|
||||
final utxosRefreshFuture = updateUTXOs();
|
||||
// if (currentHeight != storedHeight) {
|
||||
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.60, walletId));
|
||||
|
||||
_checkAlive();
|
||||
await utxosRefreshFuture;
|
||||
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.70, walletId));
|
||||
|
||||
_checkAlive();
|
||||
await fetchFuture;
|
||||
|
||||
// TODO: [prio=low] handle this differently. Extra modification of this file for coin specific functionality should be avoided.
|
||||
if (this is PaynymInterface && codesToCheck.isNotEmpty) {
|
||||
_checkAlive();
|
||||
await (this as PaynymInterface)
|
||||
.checkForNotificationTransactionsTo(codesToCheck);
|
||||
// check utxos again for notification outputs
|
||||
_checkAlive();
|
||||
await updateUTXOs();
|
||||
}
|
||||
_checkAlive();
|
||||
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.80, walletId));
|
||||
|
||||
// await getAllTxsToWatch();
|
||||
_checkAlive();
|
||||
|
||||
// TODO: [prio=low] handle this differently. Extra modification of this file for coin specific functionality should be avoided.
|
||||
if (this is LelantusInterface) {
|
||||
if (info.otherData[WalletInfoKeys.enableLelantusScanning] as bool? ??
|
||||
false) {
|
||||
await (this as LelantusInterface).refreshLelantusData();
|
||||
_checkAlive();
|
||||
}
|
||||
}
|
||||
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.90, walletId));
|
||||
|
||||
_checkAlive();
|
||||
await updateBalance();
|
||||
|
||||
_checkAlive();
|
||||
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(1.0, walletId));
|
||||
GlobalEventBus.instance.fire(
|
||||
WalletSyncStatusChangedEvent(
|
||||
WalletSyncStatus.synced,
|
||||
walletId,
|
||||
cryptoCurrency,
|
||||
),
|
||||
);
|
||||
|
||||
if (shouldAutoSync) {
|
||||
_periodicRefreshTimer ??=
|
||||
Timer.periodic(const Duration(seconds: 150), (timer) async {
|
||||
// chain height check currently broken
|
||||
// if ((await chainHeight) != (await storedChainHeight)) {
|
||||
tAlive = false; // interrupt timer as its not needed anymore
|
||||
|
||||
// TODO: [prio=med] some kind of quick check if wallet needs to refresh to replace the old refreshIfThereIsNewData call
|
||||
// if (await refreshIfThereIsNewData()) {
|
||||
unawaited(refresh());
|
||||
|
||||
// }
|
||||
// }
|
||||
});
|
||||
}
|
||||
completer.complete();
|
||||
} catch (error, strace) {
|
||||
GlobalEventBus.instance.fire(
|
||||
NodeConnectionStatusChangedEvent(
|
||||
NodeConnectionStatus.disconnected,
|
||||
walletId,
|
||||
cryptoCurrency,
|
||||
),
|
||||
);
|
||||
GlobalEventBus.instance.fire(
|
||||
WalletSyncStatusChangedEvent(
|
||||
WalletSyncStatus.unableToSync,
|
||||
walletId,
|
||||
cryptoCurrency,
|
||||
),
|
||||
);
|
||||
Logging.instance.log(
|
||||
"Caught exception in refreshWalletData(): $error\n$strace",
|
||||
level: LogLevel.Error,
|
||||
);
|
||||
completer.completeError(error, strace);
|
||||
} finally {
|
||||
t.cancel();
|
||||
refreshMutex.release();
|
||||
if (!completer.isCompleted) {
|
||||
completer.completeError(
|
||||
"finally block hit before completer completed",
|
||||
StackTrace.current,
|
||||
);
|
||||
}
|
||||
|
||||
Logging.instance.log(
|
||||
"Refresh for "
|
||||
|
|
30
pubspec.lock
30
pubspec.lock
|
@ -370,10 +370,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf
|
||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.0"
|
||||
version: "1.18.0"
|
||||
connectivity_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -1142,18 +1142,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06"
|
||||
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.7"
|
||||
version: "10.0.5"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379"
|
||||
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.8"
|
||||
version: "3.0.5"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1718,7 +1718,7 @@ packages:
|
|||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
version: "0.0.99"
|
||||
socks5_proxy:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -1854,10 +1854,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3"
|
||||
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
version: "1.2.0"
|
||||
string_validator:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -1886,26 +1886,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: test
|
||||
sha256: "713a8789d62f3233c46b4a90b174737b2c04cb6ae4500f2aa8b1be8f03f5e67f"
|
||||
sha256: "7ee44229615f8f642b68120165ae4c2a75fe77ae2065b1e55ae4711f6cf0899e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.25.8"
|
||||
version: "1.25.7"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c"
|
||||
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.3"
|
||||
version: "0.7.2"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
sha256: "12391302411737c176b0b5d6491f466b0dd56d4763e347b6714efbaa74d7953d"
|
||||
sha256: "55ea5a652e38a1dfb32943a7973f3681a60f872f8c3a05a14664ad54ef9c6696"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.5"
|
||||
version: "0.6.4"
|
||||
tezart:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
@ -49,7 +49,9 @@ const _appDataDirName = "campfire";
|
|||
const _shortDescriptionText = "Your privacy. Your wallet. Your Firo.";
|
||||
const _commitHash = "$BUILT_COMMIT_HASH";
|
||||
|
||||
const Set<AppFeature> _features = {};
|
||||
const Set<AppFeature> _features = {
|
||||
AppFeature.swap
|
||||
};
|
||||
|
||||
const ({String light, String dark})? _appIconAsset = (
|
||||
light: "assets/in_app_logo_icons/campfire-icon_light.svg",
|
||||
|
@ -60,4 +62,6 @@ final List<CryptoCurrency> _supportedCoins = List.unmodifiable([
|
|||
Firo(CryptoCurrencyNetwork.main),
|
||||
]);
|
||||
|
||||
final ({String from, String to}) _swapDefaults = (from: "BTC", to: "FIRO");
|
||||
|
||||
EOF
|
|
@ -64,4 +64,6 @@ final List<CryptoCurrency> _supportedCoins = List.unmodifiable([
|
|||
BitcoinFrost(CryptoCurrencyNetwork.test4),
|
||||
]);
|
||||
|
||||
final ({String from, String to}) _swapDefaults = (from: "BTC", to: "XMR");
|
||||
|
||||
EOF
|
|
@ -85,4 +85,6 @@ final List<CryptoCurrency> _supportedCoins = List.unmodifiable([
|
|||
Stellar(CryptoCurrencyNetwork.test),
|
||||
]);
|
||||
|
||||
final ({String from, String to}) _swapDefaults = (from: "BTC", to: "XMR");
|
||||
|
||||
EOF
|
Loading…
Reference in a new issue