Merge branch 'staging' into rylee-tmp

This commit is contained in:
ryleedavis 2022-08-29 12:42:34 -06:00
commit 3764494832
21 changed files with 893 additions and 504 deletions

View file

@ -1,5 +1,6 @@
#should deny
name: Test name: Test
on: [push, pull_request] on: [pull_request]
jobs: jobs:
test: test:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
@ -13,6 +14,10 @@ jobs:
uses: subosito/flutter-action@v2 uses: subosito/flutter-action@v2
- name: Checkout submodules - name: Checkout submodules
run: git submodule update --init --recursive run: git submodule update --init --recursive
- name: Build Lelantus
run: |
cd crypto_plugins/flutter_liblelantus/scripts/linux/
./build_all.sh
- name: Get dependencies - name: Get dependencies
run: flutter pub get run: flutter pub get
- name: Create temp files - name: Create temp files
@ -58,7 +63,13 @@ jobs:
# - name: Analyze # - name: Analyze
# run: flutter analyze # run: flutter analyze
- name: Test - name: Test
run: flutter test run: flutter test --coverage
- name: Upload to code coverage
uses: codecov/codecov-action@v1.2.2
if: success() || failure()
with:
token: ${{secrets.CODECOV_TOKEN}}
file: coverage/lcov.info
- name: Delete temp files - name: Delete temp files
run: | run: |
Remove-Item -Path $env:CHANGE_NOW; Remove-Item -Path $env:CHANGE_NOW;

View file

@ -1,3 +1,5 @@
[![codecov](https://codecov.io/gh/cypherstack/stack_wallet/branch/main/graph/badge.svg?token=PM1N56UTEW)](https://codecov.io/gh/cypherstack/stack_wallet)
# Stack Wallet # Stack Wallet
put details here put details here
@ -8,13 +10,14 @@ put features here
## Build and run ## Build and run
### Prerequisites ### Prerequisites
- Flutter 3.0.5
- Flutter SDK Requirement (>=2.12.0, up until <3.0.0) - Flutter SDK Requirement (>=2.12.0, up until <3.0.0)
- Android/iOS dev setup (Android Studio, xCode and subsequent dependencies) - Android/iOS dev setup (Android Studio, xCode and subsequent dependencies)
After that download the project and init the submodules After that download the project and init the submodules
``` ```
git clone https://github.com/cypherstack/Campfire.git git clone https://github.com/cypherstack/stack_wallet.git
cd Campfire cd stack_wallet
git submodule update --init --recursive git submodule update --init --recursive
``` ```

View file

@ -87,7 +87,7 @@ linter:
no_leading_underscores_for_local_identifiers: false no_leading_underscores_for_local_identifiers: false
no_leading_underscores_for_library_prefixes: false no_leading_underscores_for_library_prefixes: false
avoid_print: true avoid_print: true
unawaited_futures: false unawaited_futures: true
avoid_double_and_int_checks: false avoid_double_and_int_checks: false
constant_identifier_names: false constant_identifier_names: false
# avoid_print: false # Uncomment to disable the `avoid_print` rule # avoid_print: false # Uncomment to disable the `avoid_print` rule

View file

@ -53,6 +53,15 @@ android {
ndk { ndk {
abiFilters "x86_64","armeabi-v7a", "arm64-v8a" abiFilters "x86_64","armeabi-v7a", "arm64-v8a"
} }
externalNativeBuild {
cmake {
arguments "-DANDROID_STL=c++_shared", '-DBUILD_TESTING=OFF', "-DANDROID_TOOLCHAIN=clang -v"
cppFlags "-frtti -fexceptions -v -DANDROID -std=c++17"
// cppFlags "-std=c++11"
version "3.10.2"
}
}
} }
signingConfigs { signingConfigs {

@ -1 +1 @@
Subproject commit 78533fa427ffc582b83cb67a766c8d38fac2abd8 Subproject commit 6242046217abf47b61d9397ae447632b06f853fa

@ -1 +1 @@
Subproject commit 771175e87c629ca1d8db7a4417191bd15012d52f Subproject commit cd6f9cf62afcb6c1e55b16a76374a8577d85352f

View file

@ -449,7 +449,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 37; CURRENT_PROJECT_VERSION = 42;
DEVELOPMENT_TEAM = 4DQKUWSG6C; DEVELOPMENT_TEAM = 4DQKUWSG6C;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
@ -503,7 +503,7 @@
"$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**", "$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**",
"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs", "$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs",
); );
MARKETING_VERSION = 1.4.30; MARKETING_VERSION = 1.4.34;
ONLY_ACTIVE_ARCH = NO; ONLY_ACTIVE_ARCH = NO;
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet; PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@ -633,7 +633,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 37; CURRENT_PROJECT_VERSION = 42;
DEVELOPMENT_TEAM = 4DQKUWSG6C; DEVELOPMENT_TEAM = 4DQKUWSG6C;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
@ -687,7 +687,7 @@
"$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**", "$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**",
"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs", "$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs",
); );
MARKETING_VERSION = 1.4.30; MARKETING_VERSION = 1.4.34;
ONLY_ACTIVE_ARCH = NO; ONLY_ACTIVE_ARCH = NO;
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet; PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@ -709,7 +709,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 37; CURRENT_PROJECT_VERSION = 42;
DEVELOPMENT_TEAM = 4DQKUWSG6C; DEVELOPMENT_TEAM = 4DQKUWSG6C;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
@ -763,7 +763,7 @@
"$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**", "$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**",
"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs", "$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs",
); );
MARKETING_VERSION = 1.4.30; MARKETING_VERSION = 1.4.34;
ONLY_ACTIVE_ARCH = NO; ONLY_ACTIVE_ARCH = NO;
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet; PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";

View file

@ -176,7 +176,14 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
late final Completer<void> loadingCompleter; late final Completer<void> loadingCompleter;
bool didLoad = false;
Future<void> load() async { Future<void> load() async {
if (didLoad) {
return;
}
didLoad = true;
await DB.instance.init(); await DB.instance.init();
_notificationsService = ref.read(notificationsProvider); _notificationsService = ref.read(notificationsProvider);

View file

@ -1,5 +1,3 @@
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/add_wallet_views/add_wallet_view/sub_widgets/coin_select_item.dart'; import 'package:stackwallet/pages/add_wallet_views/add_wallet_view/sub_widgets/coin_select_item.dart';
@ -15,15 +13,10 @@ class AddWalletView extends StatelessWidget {
static const routeName = "/addWallet"; static const routeName = "/addWallet";
final _coins = Coin.values;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
List<Coin> coins = _coins; List<Coin> coins = [...Coin.values];
if (Platform.isIOS) { coins.remove(Coin.firoTestNet);
coins = _coins;
}
debugPrint("BUILD: $runtimeType");
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
leading: AppBarBackButton( leading: AppBarBackButton(

View file

@ -1,7 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:collection'; import 'dart:collection';
import 'dart:math';
import 'dart:io'; import 'dart:io';
import 'dart:math';
import 'package:bip39/bip39.dart' as bip39; import 'package:bip39/bip39.dart' as bip39;
import 'package:bip39/src/wordlists/english.dart' as bip39wordlist; import 'package:bip39/src/wordlists/english.dart' as bip39wordlist;
@ -26,6 +26,7 @@ import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
import 'package:stackwallet/utilities/cfcolors.dart'; import 'package:stackwallet/utilities/cfcolors.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart'; import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/custom_text_selection_controls.dart';
import 'package:stackwallet/utilities/default_nodes.dart'; import 'package:stackwallet/utilities/default_nodes.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/flush_bar_type.dart'; import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
@ -70,25 +71,61 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
final ScrollController controller = ScrollController(); final ScrollController controller = ScrollController();
final List<TextEditingController> _controllers = []; final List<TextEditingController> _controllers = [];
// late final TextEditingController _heightController;
final List<FormInputStatus> _inputStatuses = []; final List<FormInputStatus> _inputStatuses = [];
// late final FocusNode _heightFocusNode;
late final BarcodeScannerInterface scanner; late final BarcodeScannerInterface scanner;
late final TextSelectionControls textSelectionControls;
Future<void> onControlsPaste(TextSelectionDelegate delegate) async {
final data = await widget.clipboard.getData(Clipboard.kTextPlain);
if (data?.text == null) {
return;
}
final text = data!.text!.trim();
if (text.isEmpty || _controllers.isEmpty) {
delegate.pasteText(SelectionChangedCause.toolbar);
return;
}
final words = text.split(" ");
if (words.isEmpty) {
delegate.pasteText(SelectionChangedCause.toolbar);
return;
}
if (words.length == 1) {
_controllers.first.text = words.first;
if (_isValidMnemonicWord(words.first.toLowerCase())) {
setState(() {
_inputStatuses.first = FormInputStatus.valid;
});
} else {
setState(() {
_inputStatuses.first = FormInputStatus.invalid;
});
}
return;
}
_clearAndPopulateMnemonic(words);
}
@override @override
void initState() { void initState() {
_seedWordCount = widget.seedWordsLength; _seedWordCount = widget.seedWordsLength;
// _heightFocusNode = FocusNode(); textSelectionControls = Platform.isIOS
? CustomCupertinoTextSelectionControls(onPaste: onControlsPaste)
: CustomMaterialTextSelectionControls(onPaste: onControlsPaste);
scanner = widget.barcodeScanner; scanner = widget.barcodeScanner;
for (int i = 0; i < _seedWordCount; i++) { for (int i = 0; i < _seedWordCount; i++) {
_controllers.add(TextEditingController()); _controllers.add(TextEditingController());
_inputStatuses.add(FormInputStatus.empty); _inputStatuses.add(FormInputStatus.empty);
} }
// _heightController = TextEditingController();
super.initState(); super.initState();
} }
@ -97,8 +134,7 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
for (var element in _controllers) { for (var element in _controllers) {
element.dispose(); element.dispose();
} }
// _heightController.dispose();
// _heightFocusNode.dispose();
super.dispose(); super.dispose();
} }
@ -404,6 +440,9 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
_inputStatuses[i] = FormInputStatus.empty; _inputStatuses[i] = FormInputStatus.empty;
}); });
} }
controller.animateTo(controller.position.maxScrollExtent,
duration: const Duration(milliseconds: 300), curve: Curves.decelerate);
} }
@override @override
@ -442,18 +481,8 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
), ),
onPressed: () async { onPressed: () async {
try { try {
// ref
// .read(shouldShowLockscreenOnResumeStateProvider.state)
// .state = false;
final qrResult = await scanner.scan(); final qrResult = await scanner.scan();
// Future<void>.delayed(
// const Duration(seconds: 2),
// () => ref
// .read(shouldShowLockscreenOnResumeStateProvider.state)
// .state = true,
// );
final results = final results =
AddressUtils.decodeQRSeedData(qrResult.rawContent); AddressUtils.decodeQRSeedData(qrResult.rawContent);
@ -474,9 +503,6 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
} }
} }
} on PlatformException catch (e) { } on PlatformException catch (e) {
// ref
// .read(shouldShowLockscreenOnResumeStateProvider.state)
// .state = true;
// likely failed to get camera permissions // likely failed to get camera permissions
Logging.instance.log("Restore wallet qr scan failed: $e", Logging.instance.log("Restore wallet qr scan failed: $e",
level: LogLevel.Warning); level: LogLevel.Warning);
@ -512,9 +538,6 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
final content = data.text!.trim(); final content = data.text!.trim();
final list = content.split(" "); final list = content.split(" ");
_clearAndPopulateMnemonic(list); _clearAndPopulateMnemonic(list);
controller.animateTo(controller.position.maxScrollExtent,
duration: const Duration(milliseconds: 300),
curve: Curves.decelerate);
} }
}, },
), ),
@ -572,6 +595,8 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
_inputStatuses[i - 1], "$i"), _inputStatuses[i - 1], "$i"),
autovalidateMode: autovalidateMode:
AutovalidateMode.onUserInteraction, AutovalidateMode.onUserInteraction,
selectionControls:
i == 1 ? textSelectionControls : null,
onChanged: (value) { onChanged: (value) {
if (value.isEmpty) { if (value.isEmpty) {
setState(() { setState(() {

View file

@ -48,7 +48,9 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
ref.refresh(addressBookFilterProvider); ref.refresh(addressBookFilterProvider);
if (widget.coin == null) { if (widget.coin == null) {
final coins = Coin.values.where((e) => !(e == Coin.epicCash)).toList(); List<Coin> coins =
Coin.values.where((e) => !(e == Coin.epicCash)).toList();
coins.remove(Coin.firoTestNet);
bool showTestNet = ref.read(prefsChangeNotifierProvider).showTestNetCoins; bool showTestNet = ref.read(prefsChangeNotifierProvider).showTestNetCoins;

View file

@ -23,7 +23,8 @@ class _AddressBookFilterViewState extends ConsumerState<AddressBookFilterView> {
@override @override
void initState() { void initState() {
final coins = Coin.values; List<Coin> coins = [...Coin.values];
coins.remove(Coin.firoTestNet);
bool showTestNet = ref.read(prefsChangeNotifierProvider).showTestNetCoins; bool showTestNet = ref.read(prefsChangeNotifierProvider).showTestNetCoins;

View file

@ -14,6 +14,8 @@ class CoinSelectSheet extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final maxHeight = MediaQuery.of(context).size.height * 0.60; final maxHeight = MediaQuery.of(context).size.height * 0.60;
var coins_ = [...Coin.values];
coins_.remove(Coin.firoTestNet);
return Container( return Container(
decoration: const BoxDecoration( decoration: const BoxDecoration(
color: CFColors.white, color: CFColors.white,
@ -68,10 +70,10 @@ class CoinSelectSheet extends StatelessWidget {
return ListView.builder( return ListView.builder(
shrinkWrap: true, shrinkWrap: true,
itemCount: showTestNet itemCount: showTestNet
? Coin.values.length ? coins_.length
: Coin.values.length - kTestNetCoinCount, : coins_.length - kTestNetCoinCount,
itemBuilder: (builderContext, index) { itemBuilder: (builderContext, index) {
final coin = Coin.values[index]; final coin = coins_[index];
return Padding( return Padding(
padding: const EdgeInsets.symmetric(vertical: 4), padding: const EdgeInsets.symmetric(vertical: 4),
child: RawMaterialButton( child: RawMaterialButton(

View file

@ -23,11 +23,12 @@ class ManageNodesView extends ConsumerStatefulWidget {
} }
class _ManageNodesViewState extends ConsumerState<ManageNodesView> { class _ManageNodesViewState extends ConsumerState<ManageNodesView> {
List<Coin> _coins = Coin.values; List<Coin> _coins = [...Coin.values];
@override @override
void initState() { void initState() {
_coins = _coins.toList(); _coins = _coins.toList();
_coins.remove(Coin.firoTestNet);
super.initState(); super.initState();
} }

View file

@ -726,7 +726,7 @@ Future<String> _getMintScriptWrapper(
} }
Future<void> _setTestnetWrapper(bool isTestnet) async { Future<void> _setTestnetWrapper(bool isTestnet) async {
setTestnet(isTestnet); // setTestnet(isTestnet);
} }
/// Handles a single instance of a firo wallet /// Handles a single instance of a firo wallet
@ -2893,6 +2893,7 @@ class FiroWallet extends CoinServiceAPI {
} }
if (Platform.environment["FLUTTER_TEST"] == "true" || integrationTestFlag) { if (Platform.environment["FLUTTER_TEST"] == "true" || integrationTestFlag) {
perBatch = 10; perBatch = 10;
numberOfThreads = 4;
} }
final receiveDerivationsString = final receiveDerivationsString =

View file

@ -5,6 +5,7 @@ import 'package:cw_core/monero_amount_format.dart';
import 'package:cw_core/monero_transaction_priority.dart'; import 'package:cw_core/monero_transaction_priority.dart';
import 'package:cw_core/node.dart'; import 'package:cw_core/node.dart';
import 'package:cw_core/pending_transaction.dart'; import 'package:cw_core/pending_transaction.dart';
import 'package:cw_core/sync_status.dart';
import 'package:cw_core/transaction_direction.dart'; import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_credentials.dart';
@ -24,6 +25,7 @@ import 'package:flutter_libmonero/monero/monero.dart';
import 'package:flutter_libmonero/view_model/send/output.dart' as monero_output; import 'package:flutter_libmonero/view_model/send/output.dart' as monero_output;
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:http/http.dart'; import 'package:http/http.dart';
import 'package:mutex/mutex.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:stackwallet/hive/db.dart'; import 'package:stackwallet/hive/db.dart';
@ -121,10 +123,10 @@ class MoneroWallet extends CoinServiceAPI {
node: Node(uri: "$host:${node.port}", type: WalletType.monero)); node: Node(uri: "$host:${node.port}", type: WalletType.monero));
// TODO: is this sync call needed? Do we need to notify ui here? // TODO: is this sync call needed? Do we need to notify ui here?
walletBase?.startSync(); await walletBase?.startSync();
if (shouldRefresh) { if (shouldRefresh) {
refresh(); await refresh();
} }
} }
@ -142,13 +144,37 @@ class MoneroWallet extends CoinServiceAPI {
Future<List<String>> get mnemonic => _getMnemonicList(); Future<List<String>> get mnemonic => _getMnemonicList();
Future<int> get currentNodeHeight async { Future<int> get currentNodeHeight async {
int currentHeight = await getNodeHeight(); try {
if (walletBase!.syncStatus! is SyncedSyncStatus &&
walletBase!.syncStatus!.progress() == 1.0) {
return await walletBase!.getNodeHeight();
}
} catch (e, s) {}
int _height = -1;
try {
_height = (walletBase!.syncStatus as SyncingSyncStatus).height;
} catch (e, s) {
Logging.instance.log("$e $s", level: LogLevel.Warning);
}
int blocksRemaining = -1;
try {
blocksRemaining =
(walletBase!.syncStatus as SyncingSyncStatus).blocksLeft;
} catch (e, s) {
Logging.instance.log("$e $s", level: LogLevel.Warning);
}
int currentHeight = _height + blocksRemaining;
if (_height == -1 || blocksRemaining == -1) {
currentHeight = int64MaxValue;
}
final cachedHeight = DB.instance final cachedHeight = DB.instance
.get<dynamic>(boxName: walletId, key: "storedNodeHeight") as int? ?? .get<dynamic>(boxName: walletId, key: "storedNodeHeight") as int? ??
0; 0;
if (currentHeight > cachedHeight) { if (currentHeight > cachedHeight && currentHeight != int64MaxValue) {
DB.instance.put<dynamic>( await DB.instance.put<dynamic>(
boxName: walletId, key: "storedNodeHeight", value: currentHeight); boxName: walletId, key: "storedNodeHeight", value: currentHeight);
return currentHeight; return currentHeight;
} else { } else {
@ -156,29 +182,34 @@ class MoneroWallet extends CoinServiceAPI {
} }
} }
int get currentSyncingHeight { Future<int> get currentSyncingHeight async {
//TODO return the tip of the monero blockchain //TODO return the tip of the monero blockchain
final syncingHeight = getSyncingHeight(); try {
if (walletBase!.syncStatus! is SyncedSyncStatus &&
walletBase!.syncStatus!.progress() == 1.0) {
Logging.instance
.log("currentSyncingHeight lol", level: LogLevel.Warning);
return getSyncingHeight();
}
} catch (e, s) {}
int syncingHeight = -1;
try {
syncingHeight = (walletBase!.syncStatus as SyncingSyncStatus).height;
} catch (e, s) {
Logging.instance.log("$e $s", level: LogLevel.Warning);
}
final cachedHeight = final cachedHeight =
DB.instance.get<dynamic>(boxName: walletId, key: "storedSyncingHeight") DB.instance.get<dynamic>(boxName: walletId, key: "storedSyncingHeight")
as int? ?? as int? ??
0; 0;
if (syncingHeight > cachedHeight) { if (syncingHeight > cachedHeight) {
DB.instance.put<dynamic>( await DB.instance.put<dynamic>(
boxName: walletId, key: "storedSyncingHeight", value: syncingHeight); boxName: walletId, key: "storedSyncingHeight", value: syncingHeight);
return syncingHeight; return syncingHeight;
} else { } else {
return cachedHeight; return cachedHeight;
} }
// try {
// final result = await _electrumXClient.getBlockHeadTip();
// return result["height"];
// } catch (e, s) {
// Logging.instance.log("Exception caught in chainHeight: $e\n$s");
// return -1;
// }
} }
Future<void> updateStoredChainHeight({required int newHeight}) async { Future<void> updateStoredChainHeight({required int newHeight}) async {
@ -270,63 +301,78 @@ class MoneroWallet extends CoinServiceAPI {
Timer? syncPercentTimer; Timer? syncPercentTimer;
void stopSyncPercentTimer() { Mutex syncHeightMutex = Mutex();
Future<void> stopSyncPercentTimer() async {
syncPercentTimer?.cancel(); syncPercentTimer?.cancel();
syncPercentTimer = null; syncPercentTimer = null;
} }
void startSyncPercentTimer() { Future<void> startSyncPercentTimer() async {
if (syncPercentTimer != null) {
return;
}
syncPercentTimer?.cancel(); syncPercentTimer?.cancel();
GlobalEventBus.instance GlobalEventBus.instance
.fire(RefreshPercentChangedEvent(highestPercentCached, walletId)); .fire(RefreshPercentChangedEvent(highestPercentCached, walletId));
syncPercentTimer = Timer.periodic(const Duration(seconds: 3), (_) async { syncPercentTimer = Timer.periodic(const Duration(seconds: 30), (_) async {
// int restoreheight = walletBase!.walletInfo.restoreHeight ?? 0; if (syncHeightMutex.isLocked) {
int _height = currentSyncingHeight;
int _currentHeight = await currentNodeHeight;
final int blocksRemaining = _currentHeight - _height;
GlobalEventBus.instance
.fire(BlocksRemainingEvent(blocksRemaining, walletId));
if (blocksRemaining <= 1 && _currentHeight > 0 && _height > 0) {
stopSyncPercentTimer();
GlobalEventBus.instance.fire(
WalletSyncStatusChangedEvent(
WalletSyncStatus.synced,
walletId,
coin,
),
);
return; return;
} }
await syncHeightMutex.protect(() async {
// int restoreheight = walletBase!.walletInfo.restoreHeight ?? 0;
int _height = await currentSyncingHeight;
int _currentHeight = await currentNodeHeight;
double progress = 0;
try {
progress = walletBase!.syncStatus!.progress();
} catch (e, s) {
Logging.instance.log("$e $s", level: LogLevel.Warning);
}
// for some reason this can be 0 which screws up the percent calculation final int blocksRemaining = _currentHeight - _height;
// int64MaxValue is NOT the best value to use here
if (_currentHeight < 1) {
_currentHeight = int64MaxValue;
}
if (_height < 1) { GlobalEventBus.instance
_height = 1; .fire(BlocksRemainingEvent(blocksRemaining, walletId));
}
double restorePercent = (_height / _currentHeight).clamp(0.0, 1.0); if (progress == 1 && _currentHeight > 0 && _height > 0) {
double highestPercent = highestPercentCached; await stopSyncPercentTimer();
GlobalEventBus.instance.fire(
WalletSyncStatusChangedEvent(
WalletSyncStatus.synced,
walletId,
coin,
),
);
return;
}
Logging.instance.log( // for some reason this can be 0 which screws up the percent calculation
"currentSyncingHeight: $_height, nodeHeight: $_currentHeight, restorePercent: $restorePercent, highestPercentCached: $highestPercentCached", // int64MaxValue is NOT the best value to use here
level: LogLevel.Info); if (_currentHeight < 1) {
_currentHeight = int64MaxValue;
}
if (restorePercent > 0 && restorePercent <= 1) { if (_height < 1) {
if (restorePercent > highestPercent) { _height = 1;
}
double restorePercent = progress;
double highestPercent = highestPercentCached;
Logging.instance.log(
"currentSyncingHeight: $_height, nodeHeight: $_currentHeight, restorePercent: $restorePercent, highestPercentCached: $highestPercentCached",
level: LogLevel.Info);
if (restorePercent > 0 && restorePercent <= 1) {
// if (restorePercent > highestPercent) {
highestPercent = restorePercent; highestPercent = restorePercent;
highestPercentCached = restorePercent; highestPercentCached = restorePercent;
// }
} }
}
GlobalEventBus.instance GlobalEventBus.instance
.fire(RefreshPercentChangedEvent(highestPercent, walletId)); .fire(RefreshPercentChangedEvent(highestPercent, walletId));
});
}); });
} }
@ -356,7 +402,7 @@ class MoneroWallet extends CoinServiceAPI {
} }
try { try {
startSyncPercentTimer(); await startSyncPercentTimer();
GlobalEventBus.instance.fire( GlobalEventBus.instance.fire(
WalletSyncStatusChangedEvent( WalletSyncStatusChangedEvent(
WalletSyncStatus.syncing, WalletSyncStatus.syncing,
@ -365,26 +411,30 @@ class MoneroWallet extends CoinServiceAPI {
), ),
); );
final int _currentSyncingHeight = currentSyncingHeight; final int _currentSyncingHeight = await currentSyncingHeight;
final int storedHeight = storedChainHeight; final int storedHeight = storedChainHeight;
int _currentNodeHeight = await walletBase?.getNodeHeight() ?? 0; int _currentNodeHeight = await currentNodeHeight;
if (_currentNodeHeight < 1) {
_currentNodeHeight = int64MaxValue;
}
_fetchTransactionData(); double progress = 0;
try {
progress = (walletBase!.syncStatus!).progress();
} catch (e, s) {
Logging.instance.log("$e $s", level: LogLevel.Warning);
}
await _fetchTransactionData();
bool stillSyncing = false; bool stillSyncing = false;
Logging.instance.log( Logging.instance.log(
"storedHeight: $storedHeight, _currentSyncingHeight: $_currentSyncingHeight, _currentNodeHeight: $_currentNodeHeight", "storedHeight: $storedHeight, _currentSyncingHeight: $_currentSyncingHeight, _currentNodeHeight: $_currentNodeHeight, progress: $progress, issynced: ${await walletBase!.isConnected()}",
level: LogLevel.Info); level: LogLevel.Info);
if (storedHeight + 10 < _currentNodeHeight) {
if (progress < 1.0) {
stillSyncing = true; stillSyncing = true;
} }
if (_currentSyncingHeight != storedHeight) { if (_currentSyncingHeight > storedHeight) {
// 0 is returned from monero as I assume an error????? // 0 is returned from monero as I assume an error?????
if (_currentSyncingHeight != 0) { if (_currentSyncingHeight > 0) {
// 0 failed to fetch current height??? // 0 failed to fetch current height???
await updateStoredChainHeight(newHeight: _currentSyncingHeight); await updateStoredChainHeight(newHeight: _currentSyncingHeight);
} }
@ -403,19 +453,11 @@ class MoneroWallet extends CoinServiceAPI {
"Failed to call _generateAddressForChain(0, $curIndex): $e\n$s", "Failed to call _generateAddressForChain(0, $curIndex): $e\n$s",
level: LogLevel.Error); level: LogLevel.Error);
} }
// final newTxData = await _fetchTransactionData();
final newTxData = _fetchTransactionData();
// final feeObj = _getFees();
//
_transactionData = Future(() => newTxData); _transactionData = Future(() => newTxData);
//
// this._feeObject = Future(() => feeObj);
// this._utxoData = Future(() => newUtxoData);
//
// await getAllTxsToWatch(await newTxData);
if (isActive || shouldAutoSync) { if (isActive || shouldAutoSync) {
timer ??= Timer.periodic(const Duration(seconds: 30), (timer) async { timer ??= Timer.periodic(const Duration(seconds: 60), (timer) async {
debugPrint("run timer"); debugPrint("run timer");
//TODO: check for new data and refresh if needed. if monero even needs this //TODO: check for new data and refresh if needed. if monero even needs this
// chain height check currently broken // chain height check currently broken
@ -450,7 +492,7 @@ class MoneroWallet extends CoinServiceAPI {
refreshMutex = false; refreshMutex = false;
return; return;
} }
stopSyncPercentTimer(); await stopSyncPercentTimer();
GlobalEventBus.instance.fire( GlobalEventBus.instance.fire(
WalletSyncStatusChangedEvent( WalletSyncStatusChangedEvent(
WalletSyncStatus.synced, WalletSyncStatus.synced,
@ -461,7 +503,7 @@ class MoneroWallet extends CoinServiceAPI {
refreshMutex = false; refreshMutex = false;
} catch (error, strace) { } catch (error, strace) {
refreshMutex = false; refreshMutex = false;
stopSyncPercentTimer(); await stopSyncPercentTimer();
GlobalEventBus.instance.fire( GlobalEventBus.instance.fire(
NodeConnectionStatusChangedEvent( NodeConnectionStatusChangedEvent(
NodeConnectionStatus.disconnected, NodeConnectionStatus.disconnected,
@ -500,7 +542,7 @@ class MoneroWallet extends CoinServiceAPI {
@override @override
Future<void> exit() async { Future<void> exit() async {
stopSyncPercentTimer(); await stopSyncPercentTimer();
_hasCalledExit = true; _hasCalledExit = true;
isActive = false; isActive = false;
await walletBase?.save(prioritySave: true); await walletBase?.save(prioritySave: true);
@ -552,6 +594,7 @@ class MoneroWallet extends CoinServiceAPI {
} }
Future<String> _generateAddressForChain(int chain, int index) async { Future<String> _generateAddressForChain(int chain, int index) async {
//
String address = walletBase!.getTransactionAddress(chain, index); String address = walletBase!.getTransactionAddress(chain, index);
return address; return address;
@ -688,7 +731,6 @@ class MoneroWallet extends CoinServiceAPI {
await walletBase?.connectToNode( await walletBase?.connectToNode(
node: Node(uri: "$host:${node.port}", type: WalletType.monero)); node: Node(uri: "$host:${node.port}", type: WalletType.monero));
await walletBase?.startSync(); await walletBase?.startSync();
walletBase?.getNodeHeight();
await DB.instance await DB.instance
.put<dynamic>(boxName: walletId, key: "id", value: _walletId); .put<dynamic>(boxName: walletId, key: "id", value: _walletId);
@ -806,7 +848,6 @@ class MoneroWallet extends CoinServiceAPI {
String? password; String? password;
try { try {
password = await keysStorage?.getWalletPassword(walletName: _walletId); password = await keysStorage?.getWalletPassword(walletName: _walletId);
debugPrint("password $password");
} catch (e, s) { } catch (e, s) {
debugPrint("Exception was thrown $e $s"); debugPrint("Exception was thrown $e $s");
throw Exception("Password not found $e, $s"); throw Exception("Password not found $e, $s");
@ -999,7 +1040,6 @@ class MoneroWallet extends CoinServiceAPI {
await walletBase?.connectToNode( await walletBase?.connectToNode(
node: Node(uri: "$host:${node.port}", type: WalletType.monero)); node: Node(uri: "$host:${node.port}", type: WalletType.monero));
await walletBase?.rescan(height: credentials.height); await walletBase?.rescan(height: credentials.height);
await walletBase?.getNodeHeight();
} catch (e, s) { } catch (e, s) {
Logging.instance.log( Logging.instance.log(
"Exception rethrown from recoverFromMnemonic(): $e\n$s", "Exception rethrown from recoverFromMnemonic(): $e\n$s",
@ -1113,13 +1153,12 @@ class MoneroWallet extends CoinServiceAPI {
moneroAutosaveTimer = null; moneroAutosaveTimer = null;
timer?.cancel(); timer?.cancel();
timer = null; timer = null;
stopSyncPercentTimer(); await stopSyncPercentTimer();
if (isActive) { if (isActive) {
String? password; String? password;
try { try {
password = password =
await keysStorage?.getWalletPassword(walletName: _walletId); await keysStorage?.getWalletPassword(walletName: _walletId);
debugPrint("password $password");
} catch (e, s) { } catch (e, s) {
debugPrint("Exception was thrown $e $s"); debugPrint("Exception was thrown $e $s");
throw Exception("Password not found $e, $s"); throw Exception("Password not found $e, $s");
@ -1131,9 +1170,9 @@ class MoneroWallet extends CoinServiceAPI {
final host = Uri.parse(node.host).host; final host = Uri.parse(node.host).host;
await walletBase?.connectToNode( await walletBase?.connectToNode(
node: Node(uri: "$host:${node.port}", type: WalletType.monero)); node: Node(uri: "$host:${node.port}", type: WalletType.monero));
walletBase?.startSync(); await walletBase?.startSync();
} }
refresh(); await refresh();
} }
this.isActive = isActive; this.isActive = isActive;
}; };

View file

@ -0,0 +1,22 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class CustomMaterialTextSelectionControls
extends MaterialTextSelectionControls {
CustomMaterialTextSelectionControls({required this.onPaste});
ValueChanged<TextSelectionDelegate> onPaste;
@override
Future<void> handlePaste(final TextSelectionDelegate delegate) async {
return onPaste(delegate);
}
}
class CustomCupertinoTextSelectionControls
extends CupertinoTextSelectionControls {
CustomCupertinoTextSelectionControls({required this.onPaste});
ValueChanged<TextSelectionDelegate> onPaste;
@override
Future<void> handlePaste(final TextSelectionDelegate delegate) async {
return onPaste(delegate);
}
}

View file

@ -22,7 +22,8 @@ enum Coin {
firoTestNet, firoTestNet,
} }
const int kTestNetCoinCount = 3; // remove firotestnet for now
const int kTestNetCoinCount = 2;
extension CoinExt on Coin { extension CoinExt on Coin {
String get prettyName { String get prettyName {

View file

@ -11,7 +11,7 @@ description: Stack Wallet
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.4.31+38 version: 1.4.34+42
environment: environment:
sdk: ">=2.17.0 <3.0.0" sdk: ">=2.17.0 <3.0.0"

View file

@ -1,14 +1,14 @@
import 'package:flutter_test/flutter_test.dart'; // import 'package:flutter_test/flutter_test.dart';
import 'package:hive_test/hive_test.dart'; // import 'package:hive_test/hive_test.dart';
void main() { void main() {
setUp(() async { // setUp(() async {
await setUpTestHive(); // await setUpTestHive();
}); // });
//
// no migration to test yet // // no migration to test yet
//
tearDown(() async { // tearDown(() async {
await tearDownTestHive(); // await tearDownTestHive();
}); // });
} }

File diff suppressed because it is too large Load diff