diff --git a/lib/blocs/auto_receive_tx_worker.dart b/lib/blocs/auto_receive_tx_worker.dart index 6d8cbe54..6d8bd3f4 100644 --- a/lib/blocs/auto_receive_tx_worker.dart +++ b/lib/blocs/auto_receive_tx_worker.dart @@ -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/extensions.dart'; import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; @@ -33,9 +34,8 @@ class AutoReceiveTxWorker extends BaseBloc { (await zenon!.ledger.getAccountBlockByHash(currentHash))! .toAddress .toString(); - KeyPair keyPair = kKeyStore!.getKeyPair( - kDefaultAddressList.indexOf(toAddress), - ); + WalletAccount walletAccount = + await kWalletFile!.account(kDefaultAddressList.indexOf(toAddress)); AccountBlockTemplate transactionParams = AccountBlockTemplate.receive( currentHash, ); @@ -43,7 +43,7 @@ class AutoReceiveTxWorker extends BaseBloc { await AccountBlockUtils.createAccountBlock( transactionParams, 'receive transaction', - blockSigningKey: keyPair, + walletAccount: walletAccount, waitForRequiredPlasma: true, ); pool.removeFirst(); diff --git a/lib/blocs/auto_unlock_htlc_worker.dart b/lib/blocs/auto_unlock_htlc_worker.dart index 1abb70a4..1a22794c 100644 --- a/lib/blocs/auto_unlock_htlc_worker.dart +++ b/lib/blocs/auto_unlock_htlc_worker.dart @@ -9,6 +9,7 @@ import 'package:zenon_syrius_wallet_flutter/model/database/notification_type.dar import 'package:zenon_syrius_wallet_flutter/model/database/wallet_notification.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/extensions.dart'; import 'package:zenon_syrius_wallet_flutter/utils/format_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; @@ -25,7 +26,7 @@ class AutoUnlockHtlcWorker extends BaseBloc { } Future autoUnlock() async { - if (pool.isNotEmpty && !running && kKeyStore != null) { + if (pool.isNotEmpty && !running && kWalletFile != null) { running = true; Hash currentHash = pool.first; try { @@ -38,16 +39,15 @@ class AutoUnlockHtlcWorker extends BaseBloc { if (!kDefaultAddressList.contains(htlc.hashLocked.toString())) { throw 'Swap address not in default addresses. Please add the address in the addresses list.'; } - KeyPair? keyPair = kKeyStore!.getKeyPair( - kDefaultAddressList.indexOf(htlc.hashLocked.toString()), - ); + WalletAccount walletAccount = await kWalletFile!.account( + kDefaultAddressList.indexOf(htlc.hashLocked.toString())); AccountBlockTemplate transactionParams = zenon!.embedded.htlc .unlock(htlc.id, FormatUtils.decodeHexString(swap.preimage!)); AccountBlockTemplate response = await AccountBlockUtils.createAccountBlock( transactionParams, 'complete swap', - blockSigningKey: keyPair, + walletAccount: walletAccount, waitForRequiredPlasma: true, ); _sendSuccessNotification(response, htlc.hashLocked.toString()); diff --git a/lib/blocs/blocs.dart b/lib/blocs/blocs.dart index 1b1ecb40..12cde850 100644 --- a/lib/blocs/blocs.dart +++ b/lib/blocs/blocs.dart @@ -4,10 +4,10 @@ export 'auto_receive_tx_worker.dart'; export 'base_bloc.dart'; export 'base_bloc_for_reloading_indicator.dart'; export 'base_bloc_with_refresh_mixin.dart'; -export 'decrypt_key_store_bloc.dart'; +export 'decrypt_wallet_file_bloc.dart'; export 'hide_widget_status_bloc.dart'; export 'infinite_scroll_bloc.dart'; -export 'key_store_path_bloc.dart'; +export 'key_store_file_bloc.dart'; export 'lock_bloc.dart'; export 'node_sync_status_bloc.dart'; export 'notifications_bloc.dart'; diff --git a/lib/blocs/decrypt_key_store_bloc.dart b/lib/blocs/decrypt_key_store_bloc.dart deleted file mode 100644 index 323ac64b..00000000 --- a/lib/blocs/decrypt_key_store_bloc.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/keystore_utils.dart'; -import 'package:znn_sdk_dart/znn_sdk_dart.dart'; - -class DecryptKeyStoreBloc extends BaseBloc { - Future decryptKeyStoreFile(String path, String password) async { - try { - addEvent(null); - KeyStore keyStore = - await KeyStoreUtils.decryptKeyStoreFile(path, password); - addEvent(keyStore); - } catch (e, stackTrace) { - addError(e, stackTrace); - } - } -} diff --git a/lib/blocs/decrypt_wallet_file_bloc.dart b/lib/blocs/decrypt_wallet_file_bloc.dart new file mode 100644 index 00000000..6935c156 --- /dev/null +++ b/lib/blocs/decrypt_wallet_file_bloc.dart @@ -0,0 +1,15 @@ +import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/wallet_file.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/wallet_utils.dart'; + +class DecryptWalletFileBloc extends BaseBloc { + Future decryptWalletFile(String type, String path, String password) async { + try { + addEvent(null); + final walletFile = await WalletUtils.decryptWalletFile(type, path, password); + addEvent(walletFile); + } catch (e, stackTrace) { + addError(e, stackTrace); + } + } +} diff --git a/lib/blocs/hide_widget_status_bloc.dart b/lib/blocs/hide_widget_status_bloc.dart index 58776961..ba115cb9 100644 --- a/lib/blocs/hide_widget_status_bloc.dart +++ b/lib/blocs/hide_widget_status_bloc.dart @@ -2,7 +2,7 @@ import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/keystore_utils.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/wallet_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/widget_utils.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; @@ -15,7 +15,8 @@ class HideWidgetStatusBloc extends BaseBloc { try { addEvent(null); if (!isHidden) { - await KeyStoreUtils.decryptKeyStoreFile(kKeyStorePath!, password); + await WalletUtils.decryptWalletFile( + kWalletType!, kWalletPath!, password); } await _markWidgetAsHidden(widgetTitle, isHidden); addEvent(isHidden); diff --git a/lib/blocs/key_store_file_bloc.dart b/lib/blocs/key_store_file_bloc.dart new file mode 100644 index 00000000..ff09a926 --- /dev/null +++ b/lib/blocs/key_store_file_bloc.dart @@ -0,0 +1,25 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/wallet_utils.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/init_utils.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/wallet_file.dart'; +import 'package:znn_sdk_dart/znn_sdk_dart.dart'; + +class KeyStoreFileBloc extends BaseBloc { + Future getKeyStorePath( + String mnemonic, + String password, + ) async { + try { + await WalletUtils.createKeyStoreWalletFile(mnemonic, password); + await InitUtils.initWalletAfterDecryption( + Crypto.digest(utf8.encode(password))); + addEvent(kWalletFile as KeyStoreWalletFile); + } catch (e, stackTrace) { + addError(e, stackTrace); + } + } +} diff --git a/lib/blocs/key_store_path_bloc.dart b/lib/blocs/key_store_path_bloc.dart deleted file mode 100644 index 31f98cae..00000000 --- a/lib/blocs/key_store_path_bloc.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'dart:async'; - -import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/keystore_utils.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/init_utils.dart'; - -class KeyStorePathBloc extends BaseBloc { - Future getKeyStorePath( - String mnemonic, - String passphrase, - ) async { - try { - await KeyStoreUtils.createKeyStore(mnemonic, passphrase); - await InitUtils.initWalletAfterDecryption(); - addEvent(kKeyStorePath); - } catch (e, stackTrace) { - addError(e, stackTrace); - } - } -} diff --git a/lib/blocs/ledger_wallet_file_bloc.dart b/lib/blocs/ledger_wallet_file_bloc.dart new file mode 100644 index 00000000..c955f101 --- /dev/null +++ b/lib/blocs/ledger_wallet_file_bloc.dart @@ -0,0 +1,26 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/wallet_utils.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/init_utils.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/wallet_file.dart'; +import 'package:znn_sdk_dart/znn_sdk_dart.dart'; + +class LedgerWalletFileBloc extends BaseBloc { + Future getLedgerWalletPath( + String walletId, + String password, + ) async { + try { + await WalletUtils.createLedgerWalletFile( + walletId, password); + await InitUtils.initWalletAfterDecryption( + Crypto.digest(utf8.encode(password))); + addEvent(kWalletFile as LedgerWalletFile); + } catch (e, stackTrace) { + addError(e, stackTrace); + } + } +} diff --git a/lib/blocs/p2p_swap/htlc_swap/complete_htlc_swap_bloc.dart b/lib/blocs/p2p_swap/htlc_swap/complete_htlc_swap_bloc.dart index d37dda75..2fc34357 100644 --- a/lib/blocs/p2p_swap/htlc_swap/complete_htlc_swap_bloc.dart +++ b/lib/blocs/p2p_swap/htlc_swap/complete_htlc_swap_bloc.dart @@ -30,11 +30,11 @@ class CompleteHtlcSwapBloc extends BaseBloc { AccountBlockTemplate transactionParams = zenon!.embedded.htlc.unlock( Hash.parse(htlcId), FormatUtils.decodeHexString(swap.preimage!)); - KeyPair blockSigningKeyPair = kKeyStore!.getKeyPair( + WalletAccount walletAccount = await kWalletFile!.account( kDefaultAddressList.indexOf(swap.selfAddress.toString()), ); AccountBlockUtils.createAccountBlock(transactionParams, 'complete swap', - blockSigningKey: blockSigningKeyPair, waitForRequiredPlasma: true) + walletAccount: walletAccount, waitForRequiredPlasma: true) .then( (response) async { swap.state = P2pSwapState.completed; diff --git a/lib/blocs/p2p_swap/htlc_swap/join_htlc_swap_bloc.dart b/lib/blocs/p2p_swap/htlc_swap/join_htlc_swap_bloc.dart index 181ed084..33e58a96 100644 --- a/lib/blocs/p2p_swap/htlc_swap/join_htlc_swap_bloc.dart +++ b/lib/blocs/p2p_swap/htlc_swap/join_htlc_swap_bloc.dart @@ -5,6 +5,7 @@ import 'package:zenon_syrius_wallet_flutter/model/p2p_swap/p2p_swap.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/date_time_utils.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/extensions.dart'; import 'package:zenon_syrius_wallet_flutter/utils/format_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; @@ -19,7 +20,7 @@ class JoinHtlcSwapBloc extends BaseBloc { required P2pSwapChain fromChain, required P2pSwapChain toChain, required int counterHtlcExpirationTime, - }) { + }) async { try { addEvent(null); AccountBlockTemplate transactionParams = zenon!.embedded.htlc.create( @@ -31,11 +32,11 @@ class JoinHtlcSwapBloc extends BaseBloc { initialHtlc.keyMaxSize, initialHtlc.hashLock, ); - KeyPair blockSigningKeyPair = kKeyStore!.getKeyPair( + WalletAccount walletAccount = await kWalletFile!.account( kDefaultAddressList.indexOf(initialHtlc.hashLocked.toString()), ); AccountBlockUtils.createAccountBlock(transactionParams, 'join swap', - blockSigningKey: blockSigningKeyPair, waitForRequiredPlasma: true) + walletAccount: walletAccount, waitForRequiredPlasma: true) .then( (response) async { final swap = HtlcSwap( diff --git a/lib/blocs/p2p_swap/htlc_swap/reclaim_htlc_swap_funds_bloc.dart b/lib/blocs/p2p_swap/htlc_swap/reclaim_htlc_swap_funds_bloc.dart index 0dd86440..44194ea1 100644 --- a/lib/blocs/p2p_swap/htlc_swap/reclaim_htlc_swap_funds_bloc.dart +++ b/lib/blocs/p2p_swap/htlc_swap/reclaim_htlc_swap_funds_bloc.dart @@ -2,6 +2,7 @@ import 'package:zenon_syrius_wallet_flutter/blocs/base_bloc.dart'; import 'package:zenon_syrius_wallet_flutter/main.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/extensions.dart'; import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; @@ -9,17 +10,17 @@ class ReclaimHtlcSwapFundsBloc extends BaseBloc { void reclaimFunds({ required Hash htlcId, required Address selfAddress, - }) { + }) async { try { addEvent(null); AccountBlockTemplate transactionParams = zenon!.embedded.htlc.reclaim(htlcId); - KeyPair blockSigningKeyPair = kKeyStore!.getKeyPair( + WalletAccount walletAccount = await kWalletFile!.account( kDefaultAddressList.indexOf(selfAddress.toString()), ); AccountBlockUtils.createAccountBlock( transactionParams, 'reclaim swap funds', - blockSigningKey: blockSigningKeyPair, waitForRequiredPlasma: true) + walletAccount: walletAccount, waitForRequiredPlasma: true) .then( (response) { ZenonAddressUtils.refreshBalance(); diff --git a/lib/blocs/p2p_swap/htlc_swap/start_htlc_swap_bloc.dart b/lib/blocs/p2p_swap/htlc_swap/start_htlc_swap_bloc.dart index d7950869..e28e0b26 100644 --- a/lib/blocs/p2p_swap/htlc_swap/start_htlc_swap_bloc.dart +++ b/lib/blocs/p2p_swap/htlc_swap/start_htlc_swap_bloc.dart @@ -7,6 +7,7 @@ import 'package:zenon_syrius_wallet_flutter/model/p2p_swap/p2p_swap.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/date_time_utils.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/extensions.dart'; import 'package:zenon_syrius_wallet_flutter/utils/format_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; @@ -37,11 +38,11 @@ class StartHtlcSwapBloc extends BaseBloc { htlcPreimageMaxLength, hashLock.getBytes(), ); - KeyPair blockSigningKeyPair = kKeyStore!.getKeyPair( + WalletAccount walletAccount = await kWalletFile!.account( kDefaultAddressList.indexOf(selfAddress.toString()), ); AccountBlockUtils.createAccountBlock(transactionParams, 'start swap', - blockSigningKey: blockSigningKeyPair, waitForRequiredPlasma: true) + walletAccount: walletAccount, waitForRequiredPlasma: true) .then( (response) async { final swap = HtlcSwap( diff --git a/lib/blocs/transfer/send_payment_bloc.dart b/lib/blocs/transfer/send_payment_bloc.dart index c656eadb..e6dc7d56 100644 --- a/lib/blocs/transfer/send_payment_bloc.dart +++ b/lib/blocs/transfer/send_payment_bloc.dart @@ -1,6 +1,7 @@ import 'package:zenon_syrius_wallet_flutter/blocs/blocs.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/extensions.dart'; import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; @@ -13,7 +14,7 @@ class SendPaymentBloc extends BaseBloc { List? data, Token? token, AccountBlockTemplate? block, - }) { + }) async { assert( block == null && fromAddress != null && @@ -31,13 +32,13 @@ class SendPaymentBloc extends BaseBloc { amount!, data, ); - KeyPair blockSigningKeyPair = kKeyStore!.getKeyPair( + WalletAccount walletAccount = await kWalletFile!.account( kDefaultAddressList.indexOf(fromAddress), ); AccountBlockUtils.createAccountBlock( accountBlock, 'send transaction', - blockSigningKey: blockSigningKeyPair, + walletAccount: walletAccount, waitForRequiredPlasma: true, ).then( (response) { diff --git a/lib/model/database/notification_type.dart b/lib/model/database/notification_type.dart index e5ea7778..0fd48b24 100644 --- a/lib/model/database/notification_type.dart +++ b/lib/model/database/notification_type.dart @@ -49,4 +49,7 @@ enum NotificationType { @HiveField(14) delete, + + @HiveField(15) + confirm, } diff --git a/lib/model/database/notification_type.g.dart b/lib/model/database/notification_type.g.dart index 3a2a638d..f83f9a33 100644 --- a/lib/model/database/notification_type.g.dart +++ b/lib/model/database/notification_type.g.dart @@ -43,6 +43,8 @@ class NotificationTypeAdapter extends TypeAdapter { return NotificationType.changedNode; case 14: return NotificationType.delete; + case 15: + return NotificationType.confirm; default: return NotificationType.paymentSent; } @@ -96,6 +98,9 @@ class NotificationTypeAdapter extends TypeAdapter { case NotificationType.delete: writer.writeByte(14); break; + case NotificationType.confirm: + writer.writeByte(15); + break; } } diff --git a/lib/model/database/wallet_notification.dart b/lib/model/database/wallet_notification.dart index 5189d964..18814228 100644 --- a/lib/model/database/wallet_notification.dart +++ b/lib/model/database/wallet_notification.dart @@ -78,6 +78,9 @@ class WalletNotification extends HiveObject { return _getCircledIcon(Icons.link); case NotificationType.delete: return _getCircledIcon(Icons.delete_forever); + case NotificationType.confirm: + return _getCircledIcon(Icons.remove_red_eye, + iconColor: AppColors.alertNotification); default: return _getCircledIcon(MaterialCommunityIcons.arrow_top_right); } diff --git a/lib/screens/change_wallet_password_screen.dart b/lib/screens/change_wallet_password_screen.dart index 929a6a01..42bc053f 100644 --- a/lib/screens/change_wallet_password_screen.dart +++ b/lib/screens/change_wallet_password_screen.dart @@ -1,6 +1,9 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; -import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; +import 'package:zenon_syrius_wallet_flutter/blocs/decrypt_wallet_file_bloc.dart'; +import 'package:zenon_syrius_wallet_flutter/services/htlc_swaps_service.dart'; import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; @@ -145,15 +148,14 @@ class _ChangeWalletPasswordScreenState String currentPassword, String newPassword, ) async { - String mnemonic = kKeyStore!.mnemonic!; - String oldKeyStorePath = kKeyStorePath!; - await KeyStoreUtils.createKeyStore( - mnemonic, - newPassword, - keyStoreName: - '${await kKeyStore!.getKeyPair(0).address}_${DateTime.now().millisecondsSinceEpoch}', + await kWalletFile!.changePassword(currentPassword, newPassword); + final baseAddress = await WalletUtils.baseAddress(); + await HtlcSwapsService.getInstance().closeBoxes(); + await HtlcSwapsService.getInstance().openBoxes( + baseAddress.toString(), + Crypto.digest(utf8.encode(currentPassword)), + newCipherKey: Crypto.digest(utf8.encode(newPassword)), ); - await FileUtils.deleteFile(oldKeyStorePath); if (!mounted) return; Navigator.pop(context); } @@ -169,16 +171,16 @@ class _ChangeWalletPasswordScreenState } Widget _getDecryptKeyStoreFileViewModel() { - return ViewModelBuilder.reactive( + return ViewModelBuilder.reactive( onViewModelReady: (model) { - model.stream.listen((keyStore) async { - if (keyStore != null) { + model.stream.listen((walletFile) async { + if (walletFile != null) { setState(() { _currentPassErrorText = null; }); try { await _changePassword( - _newPasswordController.text, + _currentPasswordController.text, _newPasswordController.text, ); } catch (e) { @@ -208,18 +210,19 @@ class _ChangeWalletPasswordScreenState _loadingButton = _getLoadingButton(model); return _getLoadingButton(model); }, - viewModelBuilder: () => DecryptKeyStoreBloc(), + viewModelBuilder: () => DecryptWalletFileBloc(), ); } - LoadingButton _getLoadingButton(DecryptKeyStoreBloc model) { + LoadingButton _getLoadingButton(DecryptWalletFileBloc model) { return LoadingButton.onboarding( key: _loadingButtonKey, onPressed: _arePasswordsValid() ? () { _loadingButtonKey.currentState!.animateForward(); - model.decryptKeyStoreFile( - kKeyStorePath!, + model.decryptWalletFile( + kWalletType!, + kWalletPath!, _currentPasswordController.text, ); } diff --git a/lib/screens/dump_mnemonic_screen.dart b/lib/screens/dump_mnemonic_screen.dart index 48c8f5f2..9f9b7b74 100644 --- a/lib/screens/dump_mnemonic_screen.dart +++ b/lib/screens/dump_mnemonic_screen.dart @@ -103,13 +103,15 @@ class _DumpMnemonicScreenState extends State { if (_passwordController.text.isNotEmpty) { try { _continueButtonKey.currentState!.animateForward(); - await KeyStoreUtils.decryptKeyStoreFile( - kKeyStorePath!, + var walletFile = await WalletUtils.decryptWalletFile( + kWalletType!, + kWalletPath!, _passwordController.text, - ).then((keyStore) { + ); + walletFile.open().then((wallet) { setState(() { _passwordController.clear(); - _seedWords = keyStore.mnemonic!.split(' '); + _seedWords = (wallet as KeyStore).mnemonic!.split(' '); _passwordError = null; }); }); diff --git a/lib/screens/onboarding/access_wallet_screen.dart b/lib/screens/onboarding/access_wallet_screen.dart index 35b2f6e4..5cc18dd2 100644 --- a/lib/screens/onboarding/access_wallet_screen.dart +++ b/lib/screens/onboarding/access_wallet_screen.dart @@ -53,7 +53,7 @@ class _AccessWalletScreenState extends State { context: context, ), AccessWalletFluidCell( - onPressed: null, + onPressed: _onHardwareWalletButtonPressed, buttonIconLocation: 'assets/svg/ic_hardware_wallet.svg', buttonText: 'Hardware wallet', context: context, @@ -82,4 +82,11 @@ class _AccessWalletScreenState extends State { const ImportWalletSeedChoiceScreen(), ); } + + void _onHardwareWalletButtonPressed() { + NavigationUtils.push( + context, + const HardwareWalletDeviceChoiceScreen(), + ); + } } diff --git a/lib/screens/onboarding/create_key_store_screen.dart b/lib/screens/onboarding/create_key_store_screen.dart index 778a2971..bc653974 100644 --- a/lib/screens/onboarding/create_key_store_screen.dart +++ b/lib/screens/onboarding/create_key_store_screen.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/screens/screens.dart'; import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/wallet_file.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; class CreateKeyStoreScreen extends StatefulWidget { @@ -21,12 +22,12 @@ class CreateKeyStoreScreen extends StatefulWidget { } class _CreateKeyStoreScreenState extends State { - late KeyStorePathBloc _keyStorePathBloc; + late KeyStoreFileBloc _keyStoreFileBloc; @override void initState() { super.initState(); - _keyStorePathBloc = KeyStorePathBloc() + _keyStoreFileBloc = KeyStoreFileBloc() ..getKeyStorePath( widget.seed, widget.password, @@ -41,8 +42,8 @@ class _CreateKeyStoreScreenState extends State { vertical: 30.0, ), child: Center( - child: StreamBuilder( - stream: _keyStorePathBloc.stream, + child: StreamBuilder( + stream: _keyStoreFileBloc.stream, builder: (_, snapshot) { if (snapshot.hasData) { return Column( diff --git a/lib/screens/onboarding/create_ledger_screen.dart b/lib/screens/onboarding/create_ledger_screen.dart new file mode 100644 index 00000000..59e607cc --- /dev/null +++ b/lib/screens/onboarding/create_ledger_screen.dart @@ -0,0 +1,80 @@ +import 'package:flutter/material.dart'; +import 'package:zenon_syrius_wallet_flutter/blocs/ledger_wallet_file_bloc.dart'; +import 'package:zenon_syrius_wallet_flutter/screens/screens.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/wallet_file.dart'; +import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; +import 'package:znn_ledger_dart/znn_ledger_dart.dart'; + +class CreateLedgerWalletScreen extends StatefulWidget { + final LedgerWalletDefinition walletDefinition; + final String password; + final int progressBarNumLevels; + + const CreateLedgerWalletScreen( + this.walletDefinition, + this.password, { + this.progressBarNumLevels = 4, + Key? key, + }) : super(key: key); + + @override + State createState() => _CreateLedgerWalletScreenState(); +} + +class _CreateLedgerWalletScreenState extends State { + late LedgerWalletFileBloc _ledgerWalletFileBloc; + + @override + void initState() { + super.initState(); + _ledgerWalletFileBloc = LedgerWalletFileBloc() + ..getLedgerWalletPath( + widget.walletDefinition.walletId, + widget.password, + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Container( + padding: const EdgeInsets.symmetric( + vertical: 30.0, + ), + child: Center( + child: StreamBuilder( + stream: _ledgerWalletFileBloc.stream, + builder: (_, snapshot) { + if (snapshot.hasData) { + return Column( + children: [ + ProgressBar( + currentLevel: widget.progressBarNumLevels - 1, + numLevels: widget.progressBarNumLevels, + ), + Expanded( + child: NodeManagementScreen( + nodeConfirmationCallback: () { + NavigationUtils.push( + context, + WalletSuccessScreen( + progressBarNumLevels: widget.progressBarNumLevels, + ), + ); + }, + ), + ), + ], + ); + } else if (snapshot.hasError) { + return SyriusErrorWidget(snapshot.error!); + } + return const SyriusLoadingWidget(); + }, + ), + ), + ), + ); + } +} diff --git a/lib/screens/onboarding/hardware_wallet/hardware_wallet.dart b/lib/screens/onboarding/hardware_wallet/hardware_wallet.dart new file mode 100644 index 00000000..0fa29018 --- /dev/null +++ b/lib/screens/onboarding/hardware_wallet/hardware_wallet.dart @@ -0,0 +1,4 @@ +library hardware_wallet; + +export 'hardware_wallet_password_screen.dart'; +export 'hardware_wallet_device_choice_screen.dart'; diff --git a/lib/screens/onboarding/hardware_wallet/hardware_wallet_device_choice_screen.dart b/lib/screens/onboarding/hardware_wallet/hardware_wallet_device_choice_screen.dart new file mode 100644 index 00000000..be6115c0 --- /dev/null +++ b/lib/screens/onboarding/hardware_wallet/hardware_wallet_device_choice_screen.dart @@ -0,0 +1,205 @@ +import 'package:flutter/material.dart'; +import 'package:hive/hive.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; +import 'package:zenon_syrius_wallet_flutter/screens/screens.dart'; +import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; +import 'package:znn_ledger_dart/znn_ledger_dart.dart'; +import 'package:znn_sdk_dart/znn_sdk_dart.dart'; + +class HardwareWalletDeviceChoiceScreen extends StatefulWidget { + const HardwareWalletDeviceChoiceScreen({Key? key}) : super(key: key); + + @override + State createState() => + _HardwareWalletDeviceChoiceScreenState(); +} + +class _HardwareWalletDeviceChoiceScreenState + extends State { + final List _walletManagers = [LedgerWalletManager()]; + LedgerWalletDefinition? _walletDefinition; + List _walletDefinitions = []; + + final _addressNotifier = ValueNotifier(null); + String errorText = ''; + + @override + void initState() { + super.initState(); + _openHiveAddressesBox(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Container( + padding: const EdgeInsets.symmetric( + vertical: 30.0, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + children: [ + const ProgressBar( + currentLevel: 1, + numLevels: 4, + ), + const SizedBox( + height: 30.0, + ), + Text( + 'Choose your device', + style: Theme.of(context).textTheme.headlineLarge, + ), + kVerticalSpacing, + Text( + 'Please connect and unlock your device.', + style: Theme.of(context).textTheme.headlineMedium, + ), + kVerticalSpacing, + ElevatedButton( + onPressed: _getWalletDefinitions, + child: const Text( + 'Scan devices', + ), + ), + kVerticalSpacing, + ..._walletDefinitions + .map((walletDefinition) => TextButton( + onPressed: () => _openWallet( + walletDefinition, + ), + child: Text( + walletDefinition.walletName, + ), + )) + .toList(), + ], + ), + ValueListenableBuilder( + valueListenable: _addressNotifier, + builder: (context, value, _) => SizedBox( + height: 50, + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: value == null + ? const SizedBox() + : value.isEmpty + ? const CircularProgressIndicator() + : Text(value), + ), + ), + ), + Text(errorText.isNotEmpty ? 'Error: $errorText' : ''), + _getActionButtons(), + ], + ), + ), + ); + } + + void _openHiveAddressesBox() { + Hive.boxExists(kAddressesBox).then( + (bool addressesBoxExists) { + if (addressesBoxExists) { + Hive.openBox(kAddressesBox) + .then((Box addressesBox) => addressesBox.clear()); + } + }, + ); + } + + Future _getWalletDefinitions() async { + List>> futures = _walletManagers + .map((manager) => manager.getWalletDefinitions()) + .toList(); + + List> listOfDefinitions = + await Future.wait(futures); + + // Combine all the iterables into a single list using fold or expand + // For example, using fold: + List combinedList = + listOfDefinitions.fold>( + [], + (previousList, element) => previousList..addAll(element), + ); + + setState(() { + _walletDefinitions = combinedList; + _addressNotifier.value = null; + }); + } + + Future _openWallet(WalletDefinition walletDefinition) async { + Wallet? wallet; + try { + setState(() { + errorText = ''; + }); + _addressNotifier.value = ''; + for (var walletManager in _walletManagers) { + if (await walletManager.supportsWallet(walletDefinition)) { + wallet = await walletManager.getWallet(walletDefinition); + break; + } + } + final walletAccount = await wallet!.getAccount(); + final walletAddress = await walletAccount.getAddress(); + _addressNotifier.value = walletAddress.toString(); + setState(() { + _walletDefinition = walletDefinition as LedgerWalletDefinition; + }); + } catch (err) { + _mapError(err); + } finally { + if (wallet != null) { + try { + await (wallet as LedgerWallet).disconnect(); + } catch (_) {} + } + wallet = null; + } + } + + void _mapError(Object err) { + if (err is LedgerError) { + errorText = err.when( + connectionError: (error) => error, + responseError: (sw) => sw.toString(), + ); + } else { + errorText = err.toString(); + } + setState(() { + _addressNotifier.value = null; + }); + } + + Widget _getActionButtons() { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + OnboardingButton( + onPressed: () { + Navigator.pop(context); + }, + text: 'Go back', + ), + kSpacingBetweenActionButtons, + OnboardingButton( + onPressed: _walletDefinition != null + ? () { + NavigationUtils.push( + context, + HardwareWalletPasswordScreen(_walletDefinition!), + ); + } + : null, + text: 'Continue', + ), + ], + ); + } +} diff --git a/lib/screens/onboarding/hardware_wallet/hardware_wallet_password_screen.dart b/lib/screens/onboarding/hardware_wallet/hardware_wallet_password_screen.dart new file mode 100644 index 00000000..61adfa04 --- /dev/null +++ b/lib/screens/onboarding/hardware_wallet/hardware_wallet_password_screen.dart @@ -0,0 +1,153 @@ +import 'package:flutter/material.dart'; +import 'package:zenon_syrius_wallet_flutter/screens/onboarding/create_ledger_screen.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; +import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; +import 'package:znn_ledger_dart/znn_ledger_dart.dart'; + +class HardwareWalletPasswordScreen extends StatefulWidget { + final LedgerWalletDefinition walletDefinition; + + const HardwareWalletPasswordScreen(this.walletDefinition, {Key? key}) : super(key: key); + + @override + State createState() => + _HardwareWalletPasswordScreenState(); +} + +class _HardwareWalletPasswordScreenState extends State { + final TextEditingController _passwordController = TextEditingController(); + final TextEditingController _confirmPasswordController = + TextEditingController(); + + final GlobalKey _passwordKey = GlobalKey(); + final GlobalKey _confirmPasswordKey = GlobalKey(); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Container( + padding: const EdgeInsets.symmetric( + vertical: 30.0, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + children: [ + const ProgressBar( + currentLevel: 2, + numLevels: 4, + ), + const SizedBox( + height: 30.0, + ), + Text('Create a wallet password', + style: Theme.of(context).textTheme.headlineLarge), + kVerticalSpacing, + Text( + 'This is the password that will be required to unlock the wallet', + style: Theme.of(context).textTheme.headlineMedium), + const SizedBox( + height: 65.0, + ), + Column( + children: [ + Form( + key: _passwordKey, + autovalidateMode: AutovalidateMode.onUserInteraction, + child: PasswordInputField( + controller: _passwordController, + validator: InputValidators.validatePassword, + onChanged: (value) { + setState(() {}); + }, + hintText: 'Password', + ), + ), + kVerticalSpacing, + Form( + key: _confirmPasswordKey, + autovalidateMode: AutovalidateMode.onUserInteraction, + child: PasswordInputField( + controller: _confirmPasswordController, + validator: (value) => + InputValidators.checkPasswordMatch( + _passwordController.text, value), + onChanged: (value) { + setState(() {}); + }, + hintText: 'Confirm password', + ), + ), + const SizedBox( + height: 35.0, + ), + PasswordProgressBar( + password: _passwordController.text, + passwordKey: _passwordKey, + ), + ], + ), + ], + ), + const DottedBorderInfoWidget( + text: 'Use a password that has at least 8 characters, one ' + 'number, one uppercase letter, one lowercase letter and ' + 'one symbol', + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _getPassiveButton(), + kSpacingBetweenActionButtons, + _getActionButton() + ], + ), + ], + ), + ), + ); + } + + Widget _getActionButton() { + return OnboardingButton( + onPressed: _arePasswordsValid() + ? () { + NavigationUtils.push( + context, + CreateLedgerWalletScreen( + widget.walletDefinition, + _passwordController.text, + ), + ); + } + : null, + text: 'Confirm password', + ); + } + + Widget _getPassiveButton() { + return OnboardingButton( + onPressed: () { + Navigator.pop(context, false); + }, + text: 'Go back', + ); + } + + bool _arePasswordsValid() { + return InputValidators.validatePassword(_passwordController.text) == null && + InputValidators.checkPasswordMatch( + _passwordController.text, + _confirmPasswordController.text, + ) == + null; + } + + @override + void dispose() { + _confirmPasswordController.dispose(); + _passwordController.dispose(); + super.dispose(); + } +} diff --git a/lib/screens/onboarding/import_wallet/import_wallet_decrypt_screen.dart b/lib/screens/onboarding/import_wallet/import_wallet_decrypt_screen.dart index 65db377a..1b7e24b7 100644 --- a/lib/screens/onboarding/import_wallet/import_wallet_decrypt_screen.dart +++ b/lib/screens/onboarding/import_wallet/import_wallet_decrypt_screen.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; import 'package:zenon_syrius_wallet_flutter/screens/screens.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/wallet_file.dart'; import 'package:zenon_syrius_wallet_flutter/utils/utils.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; @@ -81,13 +82,14 @@ class _ImportWalletDecryptScreenState extends State { ); } - LoadingButton _getLoadingButton(DecryptKeyStoreBloc model) { + LoadingButton _getLoadingButton(DecryptWalletFileBloc model) { return LoadingButton.onboarding( key: _loadingButtonKey, onPressed: _passwordController.text.isNotEmpty ? () async { _loadingButtonKey.currentState!.animateForward(); - await model.decryptKeyStoreFile( + await model.decryptWalletFile( + kKeyStoreWalletType, widget.path, _passwordController.text, ); @@ -107,18 +109,17 @@ class _ImportWalletDecryptScreenState extends State { } _getDecryptKeyStoreFileViewModel() { - return ViewModelBuilder.reactive( + return ViewModelBuilder.reactive( onViewModelReady: (model) { - model.stream.listen((keyStore) { - if (keyStore != null) { + model.stream.listen((walletFile) { + if (walletFile != null) { _loadingButtonKey.currentState!.animateReverse(); setState(() { _passwordErrorText = null; }); - NavigationUtils.push( - context, - ImportWalletPasswordScreen(keyStore.mnemonic!), - ); + var keyStoreFile = walletFile as KeyStoreWalletFile; + NavigationUtils.push(context, + ImportWalletPasswordScreen(keyStoreFile.openSync().mnemonic!)); } }, onError: (error) { _loadingButtonKey.currentState!.animateReverse(); @@ -137,7 +138,7 @@ class _ImportWalletDecryptScreenState extends State { _loadingButton = _getLoadingButton(model); return _getLoadingButton(model); }, - viewModelBuilder: () => DecryptKeyStoreBloc(), + viewModelBuilder: () => DecryptWalletFileBloc(), ); } diff --git a/lib/screens/onboarding/onboarding.dart b/lib/screens/onboarding/onboarding.dart index cf3484e6..9041a189 100644 --- a/lib/screens/onboarding/onboarding.dart +++ b/lib/screens/onboarding/onboarding.dart @@ -5,3 +5,4 @@ export 'create_key_store_screen.dart'; export 'wallet_success_screen.dart'; export 'import_wallet/import_wallet.dart'; export 'new_wallet/new_wallet.dart'; +export 'hardware_wallet/hardware_wallet.dart'; diff --git a/lib/screens/splash_screen.dart b/lib/screens/splash_screen.dart index 142653c1..11e3b320 100644 --- a/lib/screens/splash_screen.dart +++ b/lib/screens/splash_screen.dart @@ -72,7 +72,7 @@ class _SplashScreenState extends State void _navigateToNextScreen() { _controller.stop(); - return kKeyStorePath != null + return kWalletPath != null ? _checkForDefaultNode() : Navigator.pushReplacementNamed( context, @@ -99,7 +99,7 @@ class _SplashScreenState extends State // after the user creates or imports a new wallet kWalletInitCompleted = false; await sl.get().addNotification(null); - await _deleteKeyStoreFile(); + await _deleteWalletFile(); await Hive.deleteFromDisk(); if (!mounted) return; await InitUtils.initApp(context); @@ -110,9 +110,9 @@ class _SplashScreenState extends State (boxName) async => await Hive.deleteBoxFromDisk(boxName), ); - Future _deleteKeyStoreFile() async { - await FileUtils.deleteFile(kKeyStorePath!); - kKeyStorePath = null; + Future _deleteWalletFile() async { + await FileUtils.deleteFile(kWalletPath!); + kWalletPath = null; } void _checkForDefaultNode() => sharedPrefsService!.get( diff --git a/lib/services/htlc_swaps_service.dart b/lib/services/htlc_swaps_service.dart index 2aefc1a5..ce6b2f8c 100644 --- a/lib/services/htlc_swaps_service.dart +++ b/lib/services/htlc_swaps_service.dart @@ -19,10 +19,20 @@ class HtlcSwapsService { bool get isMaxSwapsReached => _htlcSwapsBox!.length >= kMaxP2pSwapsToStore; - Future openBoxes(String htlcSwapsBoxSuffix, List cipherKey) async { + Future openBoxes(String htlcSwapsBoxSuffix, List cipherKey, + {List? newCipherKey}) async { if (_htlcSwapsBox == null || !_htlcSwapsBox!.isOpen) { _htlcSwapsBox = await Hive.openBox('${kHtlcSwapsBox}_$htlcSwapsBoxSuffix', encryptionCipher: HiveAesCipher(cipherKey)); + if (newCipherKey != null) { + final values = _htlcSwapsBox!.toMap(); + await _htlcSwapsBox!.deleteFromDisk(); + _htlcSwapsBox = await Hive.openBox( + '${kHtlcSwapsBox}_$htlcSwapsBoxSuffix', + encryptionCipher: HiveAesCipher(newCipherKey)); + _htlcSwapsBox!.putAll(values); + _htlcSwapsBox!.flush(); + } } if (_lastCheckedHtlcBlockHeightBox == null || @@ -30,6 +40,27 @@ class HtlcSwapsService { _lastCheckedHtlcBlockHeightBox = await Hive.openBox( kLastCheckedHtlcBlockBox, encryptionCipher: HiveAesCipher(cipherKey)); + if (newCipherKey != null) { + final values = _lastCheckedHtlcBlockHeightBox!.toMap(); + await _lastCheckedHtlcBlockHeightBox!.deleteFromDisk(); + _lastCheckedHtlcBlockHeightBox = await Hive.openBox( + kLastCheckedHtlcBlockBox, + encryptionCipher: HiveAesCipher(newCipherKey)); + _lastCheckedHtlcBlockHeightBox!.putAll(values); + _lastCheckedHtlcBlockHeightBox!.flush(); + } + } + } + + Future closeBoxes() async { + if (_htlcSwapsBox != null && _htlcSwapsBox!.isOpen) { + await _htlcSwapsBox!.close(); + _htlcSwapsBox = null; + } + if (_lastCheckedHtlcBlockHeightBox != null && + _lastCheckedHtlcBlockHeightBox!.isOpen) { + await _lastCheckedHtlcBlockHeightBox!.close(); + _lastCheckedHtlcBlockHeightBox = null; } } diff --git a/lib/utils/account_block_utils.dart b/lib/utils/account_block_utils.dart index 31237735..c2b63fc3 100644 --- a/lib/utils/account_block_utils.dart +++ b/lib/utils/account_block_utils.dart @@ -7,6 +7,7 @@ import 'package:zenon_syrius_wallet_flutter/model/model.dart'; import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; import 'package:zenon_syrius_wallet_flutter/utils/format_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/wallet_utils.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; class AccountBlockUtils { @@ -15,7 +16,7 @@ class AccountBlockUtils { static Future createAccountBlock( AccountBlockTemplate transactionParams, String purposeOfGeneratingPlasma, { - KeyPair? blockSigningKey, + WalletAccount? walletAccount, bool waitForRequiredPlasma = false, }) async { SyncInfo syncInfo = await zenon!.stats.syncInfo(); @@ -26,8 +27,8 @@ class AccountBlockUtils { (syncInfo.targetHeight - syncInfo.currentHeight) < 20)) : true; if (nodeIsSynced) { - Address address = (await blockSigningKey?.address ?? - await zenon!.defaultKeyPair!.address)!; + walletAccount ??= await WalletUtils.defaultAccount(); + Address address = await walletAccount.getAddress(); try { // Wait until the lock is unused. // @@ -45,7 +46,7 @@ class AccountBlockUtils { bool needPlasma = await zenon!.requiresPoW( transactionParams, - blockSigningKey: blockSigningKey, + blockSigningKey: walletAccount, ); if (needPlasma) { @@ -53,9 +54,23 @@ class AccountBlockUtils { .get() .sendPlasmaNotification(purposeOfGeneratingPlasma); } + if (kWalletType != kKeyStoreWalletType) { + sl.get().addNotification( + WalletNotification( + title: + '${BlockUtils.isSendBlock(transactionParams.blockType) ? 'Sending transaction.' : 'Receiving transaction.'} Please review the transaction on your hardware device.', + timestamp: DateTime.now().millisecondsSinceEpoch, + details: + 'Review account-block type: ${FormatUtils.extractNameFromEnum( + BlockTypeEnum.values[transactionParams.blockType], + )}', + type: NotificationType.confirm, + ), + ); + } final AccountBlockTemplate response = await zenon!.send( transactionParams, - currentKeyPair: blockSigningKey, + currentKeyPair: walletAccount, generatingPowCallback: _addEventToPowGeneratingStatusBloc, waitForRequiredPlasma: waitForRequiredPlasma, ); diff --git a/lib/utils/address_utils.dart b/lib/utils/address_utils.dart index 16791496..ff445904 100644 --- a/lib/utils/address_utils.dart +++ b/lib/utils/address_utils.dart @@ -1,22 +1,18 @@ import 'dart:async'; -import 'dart:isolate'; import 'package:flutter/material.dart'; import 'package:hive/hive.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/database/notification_type.dart'; +import 'package:zenon_syrius_wallet_flutter/model/database/wallet_notification.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/wallet_file.dart'; import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; import 'package:zenon_syrius_wallet_flutter/utils/node_utils.dart'; +import 'package:znn_ledger_dart/znn_ledger_dart.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; -class SetAddressArguments { - final KeyStore? keystore; - final SendPort port; - - SetAddressArguments(this.keystore, this.port); -} - class ZenonAddressUtils { static void refreshBalance() => sl.get().getBalanceForAllAddresses(); @@ -29,9 +25,26 @@ class ZenonAddressUtils { await Future.delayed(const Duration(milliseconds: 500)); List listAddr = []; int addrListLength = kDefaultAddressList.length; + Wallet wallet = await kWalletFile!.open(); for (int i = 0; i < numAddr; i++) { int addrListCounter = addrListLength + i; - Address? address = await kKeyStore!.getKeyPair(addrListCounter).address; + WalletAccount walletAccount = await wallet.getAccount(addrListCounter); + Address? address; + if (walletAccount is LedgerWalletAccount) { + sl.get().addNotification( + WalletNotification( + title: + 'Adding address. Please confirm the address on your hardware device.', + timestamp: DateTime.now().millisecondsSinceEpoch, + details: + 'Confirm address for account index: $addrListCounter', + type: NotificationType.confirm, + ), + ); + address = await walletAccount.getAddress(true); + } else { + address = await walletAccount.getAddress(); + } listAddr.add(address); Box addressesBox = Hive.box(kAddressesBox); await addressesBox.add(listAddr.elementAt(i).toString()); @@ -60,18 +73,6 @@ class ZenonAddressUtils { _initAddressLabels(addressLabelsBox); } - static void setAddressesFunction(SetAddressArguments args) async { - for (var element in (await Future.wait( - List>.generate( - kNumOfInitialAddresses, - (index) async => - (await args.keystore!.getKeyPair(index).address).toString()), - ))) { - args.port.send(element); - } - args.port.send('done'); - } - static Future setDefaultAddress() async { if (sharedPrefsService!.get(kDefaultAddressKey) == null) { await sharedPrefsService!.put( @@ -82,34 +83,21 @@ class ZenonAddressUtils { kSelectedAddress = sharedPrefsService!.get(kDefaultAddressKey); } - static Future setAddresses(KeyStore? keyStore) async { - final port = ReceivePort(); + static Future setAddresses(WalletFile? walletFile) async { Box addressesBox = await Hive.openBox(kAddressesBox); - final args = SetAddressArguments(keyStore, port.sendPort); if (addressesBox.isEmpty) { - Isolate.spawn( - setAddressesFunction, - args, - onError: port.sendPort, - onExit: port.sendPort, - ); - StreamSubscription? sub; - Completer completer = Completer(); - sub = port.listen( - (data) async { - if (data != null && data != 'done') { - addressesBox.add(data); - } else if (data == 'done') { - _initAddresses(addressesBox); - completer.complete(); - await sub?.cancel(); - } - }, - ); - return completer.future; - } else { - _initAddresses(addressesBox); + Wallet wallet = await walletFile!.open(); + for (var element in (await Future.wait( + List>.generate( + kNumOfInitialAddresses, + (index) async => + (await (await wallet.getAccount(index)).getAddress()) + .toString()), + ))) { + addressesBox.add(element); + } } + _initAddresses(addressesBox); } static void _initAddresses(Box addressesBox) => diff --git a/lib/utils/constants.dart b/lib/utils/constants.dart index 1cb2c11e..50e91df0 100644 --- a/lib/utils/constants.dart +++ b/lib/utils/constants.dart @@ -29,7 +29,7 @@ const SizedBox kSpacingBetweenActionButtons = SizedBox( const Size kAcceleratorProgressBarSize = Size(300.0, 10.0); // Wallet version -const String kWalletVersion = '0.1.0'; +const String kWalletVersion = '0.2.0'; // Boxes constants const String kFavoriteTokensBox = 'favourite_tokens_box'; @@ -57,6 +57,10 @@ const List kCacheBoxesToBeDeleted = [ // Wallet file name const String kNameWalletFile = 'wallet'; +// Wallet types +const String kLedgerWalletType = 'ledger'; +const String kKeyStoreWalletType = 'keyStore'; + // Github Syrius releases link const String kGithubReleasesLink = 'https://github.com/zenon-network/syrius/releases/latest'; diff --git a/lib/utils/extensions.dart b/lib/utils/extensions.dart index 80cd685a..9dec0f3b 100644 --- a/lib/utils/extensions.dart +++ b/lib/utils/extensions.dart @@ -1,5 +1,7 @@ import 'dart:math' show pow; import 'package:big_decimal/big_decimal.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/wallet_file.dart'; +import 'package:znn_sdk_dart/znn_sdk_dart.dart'; extension StringExtensions on String { String capitalize() { @@ -68,3 +70,10 @@ extension ShortString on String { '${longString.substring(longString.length - 6)}'; } } + +extension WalletFileExtensions on WalletFile { + Future account([int index = 0]) async { + final walletFile = await open(); + return await walletFile.getAccount(index); + } +} \ No newline at end of file diff --git a/lib/utils/functions.dart b/lib/utils/functions.dart index 7e620d22..9ef8c33d 100644 --- a/lib/utils/functions.dart +++ b/lib/utils/functions.dart @@ -1,9 +1,9 @@ import 'package:flutter/services.dart'; -import 'package:zenon_syrius_wallet_flutter/main.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/wallet_utils.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; Future walletSign(List message) async { - List signature = await zenon!.defaultKeyPair!.sign( + List signature = await (await WalletUtils.defaultAccount()).sign( Uint8List.fromList( message, ), diff --git a/lib/utils/global.dart b/lib/utils/global.dart index 611ee340..f79e7f8e 100644 --- a/lib/utils/global.dart +++ b/lib/utils/global.dart @@ -1,15 +1,14 @@ -import 'dart:io'; - import 'package:flutter/cupertino.dart'; import 'package:zenon_syrius_wallet_flutter/model/model.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/wallet_file.dart'; import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; -import 'package:znn_sdk_dart/znn_sdk_dart.dart'; ValueNotifier kLastWalletConnectUriNotifier = ValueNotifier(null); String? kCurrentNode; String? kSelectedAddress; -String? kKeyStorePath; +String? kWalletPath; +String? kWalletType; String? kLocalIpAddress; int? kAutoLockWalletMinutes; @@ -20,11 +19,7 @@ double? kAutoEraseWalletLimit; bool kWalletInitCompleted = false; -KeyStore? kKeyStore; - -KeyStoreManager kKeyStoreManager = KeyStoreManager( - walletPath: Directory(kKeyStorePath!), -); +WalletFile? kWalletFile; List kDbNodes = []; List kDefaultAddressList = []; diff --git a/lib/utils/init_utils.dart b/lib/utils/init_utils.dart index 527eefec..15616864 100644 --- a/lib/utils/init_utils.dart +++ b/lib/utils/init_utils.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; +import 'package:version/version.dart'; import 'package:zenon_syrius_wallet_flutter/handlers/htlc_swaps_handler.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; import 'package:zenon_syrius_wallet_flutter/services/shared_prefs_service.dart'; @@ -9,7 +10,7 @@ import 'package:zenon_syrius_wallet_flutter/services/wallet_connect_service.dart import 'package:zenon_syrius_wallet_flutter/utils/address_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/keystore_utils.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/wallet_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/node_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/widget_utils.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; @@ -21,7 +22,7 @@ class InitUtils { WidgetUtils.setTextScale(context); _setAutoEraseWalletNumAttempts(); _setAutoLockWalletTimeInterval(); - await KeyStoreUtils.setKeyStorePath(); + await WalletUtils.setWalletPathAndType(); await _setNumUnlockFailedAttempts(); await NodeUtils.setNode(); _setChainId(); @@ -72,21 +73,27 @@ class InitUtils { defaultValue: kAutoLockWalletDefaultIntervalMinutes, ); - static Future initWalletAfterDecryption() async { - await ZenonAddressUtils.setAddresses(kKeyStore); + static Future initWalletAfterDecryption(List cipherKey) async { + final walletVersion = Version.parse(sharedPrefsService! + .get(kWalletVersionKey, defaultValue: kWalletVersion)); + await ZenonAddressUtils.setAddresses(kWalletFile); await ZenonAddressUtils.setAddressLabels(); await ZenonAddressUtils.setDefaultAddress(); - zenon!.defaultKeyPair = kKeyStore!.getKeyPair( - kDefaultAddressList.indexOf(kSelectedAddress), - ); await _openFavoriteTokensBox(); await _openNotificationsBox(); await _openRecipientBox(); await NodeUtils.initWebSocketClient(); await _setWalletVersion(); - final baseAddress = await kKeyStore!.getKeyPair(0).address; - await htlcSwapsService!.openBoxes( - baseAddress.toString(), kKeyStore!.getKeyPair(0).getPrivateKey()!); + final baseAddress = await WalletUtils.baseAddress(); + if (walletVersion <= Version(0, 1, 0)) { + var wallet = await kWalletFile!.open() as KeyStore; + // Migrate to password as the cipherkey instead of the private key. + await htlcSwapsService!.openBoxes( + baseAddress.toString(), wallet.getKeyPair().getPrivateKey()!, + newCipherKey: cipherKey); + } else { + await htlcSwapsService!.openBoxes(baseAddress.toString(), cipherKey); + } sl().start(); kWalletInitCompleted = true; } diff --git a/lib/utils/keystore_utils.dart b/lib/utils/keystore_utils.dart deleted file mode 100644 index b03e84d7..00000000 --- a/lib/utils/keystore_utils.dart +++ /dev/null @@ -1,73 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:hive/hive.dart'; -import 'package:zenon_syrius_wallet_flutter/main.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; -import 'package:znn_sdk_dart/znn_sdk_dart.dart'; - -class KeyStoreUtils { - static Future decryptKeyStoreFile( - String keyStorePath, - String password, - ) async { - return await KeyFile.fromJson( - json.decode( - File(keyStorePath).readAsStringSync(), - ), - ).decrypt(password); - } - - static void _initKeyStoreConstants( - KeyStore keyStore, - String keyStorePath, - ) { - kKeyStorePath = keyStorePath; - kKeyStore = keyStore; - } - - static Future createKeyStore( - String mnemonic, - String passphrase, { - String? keyStoreName, - }) async { - KeyStoreManager keyStoreManager = - KeyStoreManager(walletPath: znnDefaultWalletDirectory); - KeyStore keyStore = KeyStore.fromMnemonic(mnemonic); - File keyStoreFile = await keyStoreManager.saveKeyStore( - keyStore, - passphrase, - name: keyStoreName, - ); - _initKeyStoreConstants( - keyStore, - keyStoreFile.path, - ); - await _saveKeyStorePath(keyStoreFile.path); - } - - static Future _saveKeyStorePath(String? keyStorePath) async { - Box keyStoreBox = await Hive.openBox(kKeyStoreBox); - await keyStoreBox.put(0, keyStorePath); - } - - static Future setKeyStorePath() async { - if (kKeyStorePath == null) { - Box keyStoreBox = await Hive.openBox(kKeyStoreBox); - if (keyStoreBox.isEmpty) { - // Here we check if the key store path is saved in another place - // and we copy that value, if it exists - String? keyStorePath = sharedPrefsService!.get(kEntropyFilePathKey); - if (keyStorePath != null) { - keyStoreBox.add(keyStorePath); - kKeyStorePath = keyStoreBox.values.first; - } - } else { - kKeyStorePath = keyStoreBox.values.first; - } - } else { - _saveKeyStorePath(kKeyStorePath); - } - } -} diff --git a/lib/utils/node_utils.dart b/lib/utils/node_utils.dart index 947ccd69..5b34f495 100644 --- a/lib/utils/node_utils.dart +++ b/lib/utils/node_utils.dart @@ -178,7 +178,7 @@ class NodeUtils { (_kHeight == 0 || result['height'] >= _kHeight + 1)) { _kHeight = result['height']; if (sl().pool.isNotEmpty && - kKeyStore != null) { + kWalletFile != null) { sl().autoReceive(); } } diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index d3d75599..1a696982 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -13,7 +13,7 @@ export 'format_utils.dart'; export 'global.dart'; export 'init_utils.dart'; export 'input_validators.dart'; -export 'keystore_utils.dart'; +export 'wallet_utils.dart'; export 'navigation_utils.dart'; export 'network_utils.dart'; export 'node_utils.dart'; diff --git a/lib/utils/wallet_file.dart b/lib/utils/wallet_file.dart new file mode 100644 index 00000000..a2398dac --- /dev/null +++ b/lib/utils/wallet_file.dart @@ -0,0 +1,147 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:hex/hex.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; +import 'package:znn_ledger_dart/znn_ledger_dart.dart'; +import 'package:znn_sdk_dart/znn_sdk_dart.dart'; +import 'package:path/path.dart' as path; + +abstract class WalletFile { + final String _path; + + static Future> read(String walletPath, String password) async { + final file = File(walletPath); + if (!file.existsSync()) { + throw InvalidWalletPath('Given wallet path does not exist ($walletPath)'); + } + return SecureFile.fromJson(json.decode(file.readAsStringSync())) + .decrypt(password); + } + + static Future write( + String walletPath, List data, String password) async { + final file = File(walletPath); + final encrypted = await SecureFile.encrypt(data, password); + file.writeAsString(json.encode(encrypted), mode: FileMode.writeOnly); + } + + WalletFile(this._path); + + String get walletPath => _path; + + String get walletType; + + Future open(); + + Future dispose(); + + Future changePassword( + String currentPassword, String newPassword) async { + var decrypted = await WalletFile.read(walletPath, currentPassword); + await WalletFile.write(walletPath, decrypted, newPassword); + } +} + +class KeyStoreWalletFile extends WalletFile { + final String walletSeed; + + static final KeyStoreManager keyStoreWalletManager = + KeyStoreManager(walletPath: znnDefaultWalletDirectory); + static KeyStore? _keyStore; + + static Future create(String mnemonic, String password, + {String? name}) async { + KeyStore wallet = KeyStore.fromMnemonic(mnemonic); + KeyStoreDefinition walletDefinition = + await keyStoreWalletManager.saveKeyStore(wallet, password, name: name); + return KeyStoreWalletFile._internal( + walletDefinition.walletId, wallet.entropy); + } + + static Future decrypt( + String walletPath, String password) async { + List decrypted = await WalletFile.read(walletPath, password); + return KeyStoreWalletFile._internal(walletPath, HEX.encode(decrypted)); + } + + KeyStoreWalletFile._internal(super.walletPath, this.walletSeed); + + KeyStore openSync() { + _keyStore ??= KeyStore.fromEntropy(walletSeed); + return _keyStore!; + } + + @override + String get walletType => kKeyStoreWalletType; + + @override + Future open() async { + return openSync(); + } + + @override + Future dispose() async { + _keyStore = null; + } +} + +class LedgerWalletFile extends WalletFile { + final String walletId; + + static final LedgerWalletManager ledgerWalletManager = LedgerWalletManager(); + static LedgerWallet? _wallet; + + static Future create(String walletId, String password, + {String? name}) async { + LedgerWallet wallet = await LedgerWallet.connect( + walletId, LedgerWalletOptions(confirmAddressByDefault: false)); + name ??= (await (await wallet.getAccount()).getAddress()).toString(); + wallet.disconnect(); + final walletPath = path.join(znnDefaultWalletDirectory.path, name); + await WalletFile.write(walletPath, utf8.encode(walletId), password); + return LedgerWalletFile._internal(walletPath, walletId); + } + + static Future decrypt( + String walletPath, String password) async { + List decrypted = await WalletFile.read(walletPath, password); + return LedgerWalletFile._internal(walletPath, utf8.decode(decrypted)); + } + + LedgerWalletFile._internal(super.walletPath, this.walletId); + + @override + String get walletType => kLedgerWalletType; + + @override + Future open([confirmAddress = false]) async { + await close(); + for (var walletDefinition + in await ledgerWalletManager.getWalletDefinitions()) { + if (walletDefinition.walletId == walletId) { + _wallet = await ledgerWalletManager.getWallet(walletDefinition) + as LedgerWallet; + return _wallet!; + } + } + throw Exception( + 'Could not find ledger device. Use the same device on which the wallet is initialized.'); + } + + Future close() async { + if (_wallet != null) { + try { + await _wallet!.disconnect(); + } catch (_) { + } finally { + _wallet = null; + } + } + } + + @override + Future dispose() async { + await close(); + } +} diff --git a/lib/utils/wallet_utils.dart b/lib/utils/wallet_utils.dart new file mode 100644 index 00000000..758ce6c0 --- /dev/null +++ b/lib/utils/wallet_utils.dart @@ -0,0 +1,97 @@ +import 'package:hive/hive.dart'; +import 'package:zenon_syrius_wallet_flutter/main.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/extensions.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/wallet_file.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; +import 'package:znn_sdk_dart/znn_sdk_dart.dart'; + +class WalletUtils { + static Future decryptWalletFile( + String walletType, String walletPath, String password) async { + if (walletType == kLedgerWalletType) { + return await LedgerWalletFile.decrypt(walletPath, password); + } else { + return await KeyStoreWalletFile.decrypt(walletPath, password); + } + } + + static Future
baseAddress() async { + return Address.parse(kDefaultAddressList.first!); + } + + static Future
defaultAddress() async { + return Address.parse(kSelectedAddress!); + } + + static Future defaultAccount() async { + return await kWalletFile! + .account(kDefaultAddressList.indexOf(kSelectedAddress)); + } + + static Future> defaultPublicKey() async { + final walletAccount = await defaultAccount(); + return await walletAccount.getPublicKey(); + } + + static Future createLedgerWalletFile( + String walletId, + String password, { + String? name, + }) async { + kWalletFile = await LedgerWalletFile.create(walletId, password, name: name); + await _storeWalletPath(kWalletFile!.walletPath); + await _storeWalletType(kWalletFile!.walletType); + } + + static Future createKeyStoreWalletFile( + String mnemonic, + String password, { + String? name, + }) async { + kWalletFile = + await KeyStoreWalletFile.create(mnemonic, password, name: name); + await _storeWalletPath(kWalletFile!.walletPath); + await _storeWalletType(kWalletFile!.walletType); + } + + static Future _storeWalletPath(String? walletPath) async { + Box keyStoreBox = await Hive.openBox(kKeyStoreBox); + await keyStoreBox.put(0, walletPath); + } + + static Future _storeWalletType(String? walletType) async { + Box keyStoreBox = await Hive.openBox(kKeyStoreBox); + await keyStoreBox.put(1, walletType); + } + + static Future setWalletPathAndType() async { + if (kWalletPath == null) { + Box keyStoreBox = await Hive.openBox(kKeyStoreBox); + if (keyStoreBox.isEmpty) { + // Here we check if the key store path is saved in another place + // and we copy that value, if it exists + String? keyStorePath = sharedPrefsService!.get(kEntropyFilePathKey); + if (keyStorePath != null) { + keyStoreBox.add(keyStorePath); + kWalletPath = keyStoreBox.values.first; + } + } else { + kWalletPath = keyStoreBox.values.first; + } + } else { + _storeWalletPath(kWalletPath); + } + + if (kWalletType == null) { + Box keyStoreBox = await Hive.openBox(kKeyStoreBox); + if (keyStoreBox.values.length > 1) { + kWalletType = keyStoreBox.getAt(1); + } else { + kWalletType = kKeyStoreWalletType; + } + } else { + _storeWalletType(kWalletType); + } + } +} diff --git a/lib/widgets/main_app_container.dart b/lib/widgets/main_app_container.dart index 55e4615e..8dca4487 100644 --- a/lib/widgets/main_app_container.dart +++ b/lib/widgets/main_app_container.dart @@ -267,8 +267,9 @@ class _MainAppContainerState extends State ); } - void _onNavigateToLock() { - kKeyStore = null; + void _onNavigateToLock() async { + if (kWalletFile != null) await kWalletFile!.dispose(); + kWalletFile = null; _navigateToLockTimer?.cancel(); } diff --git a/lib/widgets/modular_widgets/settings_widgets/addresses.dart b/lib/widgets/modular_widgets/settings_widgets/addresses.dart index dfbb91ad..eacabb3e 100644 --- a/lib/widgets/modular_widgets/settings_widgets/addresses.dart +++ b/lib/widgets/modular_widgets/settings_widgets/addresses.dart @@ -5,7 +5,6 @@ import 'package:hive/hive.dart'; import 'package:number_selector/number_selector.dart'; import 'package:provider/provider.dart'; import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; -import 'package:zenon_syrius_wallet_flutter/main.dart'; import 'package:zenon_syrius_wallet_flutter/utils/address_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/app_colors.dart'; import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; @@ -71,9 +70,6 @@ class AddressesState extends State { ); widget.accountChainStatsBloc.updateStream(); _selectedAddress = newDefaultAddress; - zenon!.defaultKeyPair = kKeyStore!.getKeyPair( - kDefaultAddressList.indexOf(newDefaultAddress), - ); } catch (e) { rethrow; } diff --git a/lib/widgets/modular_widgets/settings_widgets/backup.dart b/lib/widgets/modular_widgets/settings_widgets/backup.dart index 7104df51..cc1ce8e8 100644 --- a/lib/widgets/modular_widgets/settings_widgets/backup.dart +++ b/lib/widgets/modular_widgets/settings_widgets/backup.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; import 'package:zenon_syrius_wallet_flutter/screens/screens.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; import 'package:zenon_syrius_wallet_flutter/utils/navigation_utils.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; +import 'package:znn_sdk_dart/znn_sdk_dart.dart'; class BackupWidget extends StatefulWidget { const BackupWidget({Key? key}) : super(key: key); @@ -27,26 +29,28 @@ class _BackupWidgetState extends State { return ListView( shrinkWrap: true, children: [ - CustomExpandablePanel('Backup Wallet', _getBackupButton()), + CustomExpandablePanel('Backup Wallet', _getBackupWalletButton()), CustomExpandablePanel('Dump Mnemonic', _getDumpMnemonicButton()), ], ); } - Widget _getBackupButton() { + Widget _getBackupWalletButton() { return Center( child: SettingsButton( - onPressed: _onBackupWalletPressed, + onPressed: (kWalletType == kKeyStoreWalletType) + ? _onBackupWalletPressed + : null, text: 'Backup wallet', ), ); } - void _onBackupWalletPressed() { + void _onBackupWalletPressed() async { NavigationUtils.push( context, ExportWalletInfoScreen( - kKeyStore!.mnemonic!, + ((await kWalletFile!.open()) as KeyStore).mnemonic!, backupWalletFlow: true, ), ); @@ -55,14 +59,18 @@ class _BackupWidgetState extends State { Widget _getDumpMnemonicButton() { return Center( child: SettingsButton( - onPressed: () { - NavigationUtils.push( - context, - const DumpMnemonicScreen(), - ); - }, + onPressed: (kWalletType == kKeyStoreWalletType) + ? _onDumpMnemonicPressed + : null, text: 'Dump Mnemonic', ), ); } + + void _onDumpMnemonicPressed() { + NavigationUtils.push( + context, + const DumpMnemonicScreen(), + ); + } } diff --git a/lib/widgets/modular_widgets/settings_widgets/security.dart b/lib/widgets/modular_widgets/settings_widgets/security.dart index 50071648..3918b196 100644 --- a/lib/widgets/modular_widgets/settings_widgets/security.dart +++ b/lib/widgets/modular_widgets/settings_widgets/security.dart @@ -14,6 +14,7 @@ import 'package:zenon_syrius_wallet_flutter/utils/functions.dart'; import 'package:zenon_syrius_wallet_flutter/utils/global.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/wallet_utils.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; @@ -90,7 +91,7 @@ class _SecurityWidgetState extends State { Widget _getFutureBuilder(BuildContext context) { return FutureBuilder>( - future: zenon!.defaultKeyPair!.getPublicKey(), + future: WalletUtils.defaultPublicKey(), builder: (_, snapshot) { if (snapshot.hasError) { return SyriusErrorWidget(snapshot.error.toString()); diff --git a/lib/widgets/tab_children_widgets/lock_tab_child.dart b/lib/widgets/tab_children_widgets/lock_tab_child.dart index b0c3d627..4577774b 100644 --- a/lib/widgets/tab_children_widgets/lock_tab_child.dart +++ b/lib/widgets/tab_children_widgets/lock_tab_child.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:flutter_vector_icons/flutter_vector_icons.dart'; import 'package:zenon_syrius_wallet_flutter/main.dart'; @@ -6,7 +8,7 @@ import 'package:zenon_syrius_wallet_flutter/utils/app_colors.dart'; import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; import 'package:zenon_syrius_wallet_flutter/utils/init_utils.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/keystore_utils.dart'; +import 'package:zenon_syrius_wallet_flutter/utils/wallet_utils.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/widgets/widgets.dart'; @@ -156,15 +158,17 @@ class _LockTabChildState extends State { _actionButtonKey.currentState!.btnState == ButtonState.idle) { try { _actionButtonKey.currentState!.animateForward(); - await KeyStoreUtils.decryptKeyStoreFile( - kKeyStorePath!, + kWalletFile = await WalletUtils.decryptWalletFile( + kWalletType!, + kWalletPath!, _passwordController.text, - ).then((keyStore) => kKeyStore = keyStore); + ); if (kWalletInitCompleted == false) { setState(() { _messageToUser = 'Initializing wallet, please wait'; }); - await InitUtils.initWalletAfterDecryption(); + await InitUtils.initWalletAfterDecryption( + Crypto.digest(utf8.encode(_passwordController.text))); widget.afterInitCallback(); } else { await widget.afterUnlockCallback(_passwordController.text); diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index 16b029a0..2c99e53e 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -157,10 +157,27 @@ else() string(STRIP "${ZNN_SDK_DART_PATH}" ZNN_SDK_DART_PATH) endif() +file(STRINGS "${SYRIUS_PROJECT_DIRECTORY}/.dart_tool/package_config.json" + ZNN_LEDGER_DART_PATH REGEX "(file:).*(znn_ledger_dart).*\\/\"" ) + +if (ZNN_LEDGER_DART_PATH STREQUAL "") + file(STRINGS "${SYRIUS_PROJECT_DIRECTORY}/.dart_tool/package_config.json" + ZNN_LEDGER_DART_PATH REGEX "(rootUri).*(znn_ledger_dart).*\"" ) + string(REPLACE "\"rootUri\": \"" "" ZNN_LEDGER_DART_PATH "${ZNN_LEDGER_DART_PATH}") + string(REPLACE "/\"," "" ZNN_LEDGER_DART_PATH "${ZNN_LEDGER_DART_PATH}") + string(REPLACE "\"," "" ZNN_LEDGER_DART_PATH "${ZNN_LEDGER_DART_PATH}") + string(STRIP "${ZNN_LEDGER_DART_PATH}" ZNN_LEDGER_DART_PATH) +else() + string(REPLACE "\"rootUri\": \"file://" "" ZNN_LEDGER_DART_PATH "${ZNN_LEDGER_DART_PATH}") + string(REPLACE "/\"," "" ZNN_LEDGER_DART_PATH "${ZNN_LEDGER_DART_PATH}") + string(STRIP "${ZNN_LEDGER_DART_PATH}" ZNN_LEDGER_DART_PATH) +endif() + list(APPEND SYRIUS_LIBRARIES "${SYRIUS_PROJECT_DIRECTORY}/lib/embedded_node/blobs/libznn.so" "${ZNN_SDK_DART_PATH}/lib/src/argon2/blobs/libargon2_ffi_plugin.so" "${ZNN_SDK_DART_PATH}/lib/src/pow/blobs/libpow_links.so" + "${ZNN_LEDGER_DART_PATH}/lib/src/ledger/blobs/libledger_ffi.so" ) foreach(znn_library ${SYRIUS_LIBRARIES}) diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 42db3b89..5859d9af 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -31,6 +31,7 @@ 9631FB752956060A007ED711 /* ../lib/embedded_node/blobs/libznn.dylib in Resources */ = {isa = PBXBuildFile; fileRef = 04CC766027C81F7E00DBC357 /* ../lib/embedded_node/blobs/libznn.dylib */; }; 9631FB86295610FB007ED711 /* libargon2_ffi.dylib in Resources */ = {isa = PBXBuildFile; fileRef = 9631FB7A29560ECB007ED711 /* libargon2_ffi.dylib */; }; 9631FB87295610FF007ED711 /* libpow_links.dylib in Resources */ = {isa = PBXBuildFile; fileRef = 9631FB8229560F53007ED711 /* libpow_links.dylib */; }; + 9631FB88295610FF007ED711 /* libledger_ffi.dylib in Resources */ = {isa = PBXBuildFile; fileRef = 9631FB8A29561054007ED711 /* libledger_ffi.dylib */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -77,6 +78,7 @@ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 9631FB7A29560ECB007ED711 /* libargon2_ffi.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libargon2_ffi.dylib; sourceTree = ""; }; 9631FB8229560F53007ED711 /* libpow_links.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libpow_links.dylib; sourceTree = ""; }; + 9631FB8A29561054007ED711 /* libledger_ffi.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libledger_ffi.dylib; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; DDCA1E2CBCCC96B9436CF502 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; E5DFBCCB2F3B416824983EAB /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -129,6 +131,7 @@ children = ( 9631FB7A29560ECB007ED711 /* libargon2_ffi.dylib */, 9631FB8229560F53007ED711 /* libpow_links.dylib */, + 9631FB8A29561054007ED711 /* libledger_ffi.dylib */, 04CC766027C81F7E00DBC357 /* ../lib/embedded_node/blobs/libznn.dylib */, 33CC10F22044A3C60003C045 /* Assets.xcassets */, 33CC10F42044A3C60003C045 /* MainMenu.xib */, @@ -189,6 +192,7 @@ buildPhases = ( 26152DF600C95C6136D5E940 /* [CP] Check Pods Manifest.lock */, 96DA24F0296EB70E00545E88 /* ShellScript */, + 96DA2500296EB70F00545E88 /* ShellScript */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, @@ -258,6 +262,7 @@ files = ( 9631FB87295610FF007ED711 /* libpow_links.dylib in Resources */, 9631FB86295610FB007ED711 /* libargon2_ffi.dylib in Resources */, + 9631FB88295610FF007ED711 /* libledger_ffi.dylib in Resources */, 9631FB752956060A007ED711 /* ../lib/embedded_node/blobs/libznn.dylib in Resources */, 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, @@ -379,6 +384,23 @@ shellPath = /bin/sh; shellScript = "OUTPUT_DIRECTORY=\"./\"\n\nPACKAGE_CONFIG=\"../.dart_tool/package_config.json\"\nZNN_SDK_DART_PATH=`cat $PACKAGE_CONFIG | grep git/znn_sdk_dart | sed 's/\"rootUri\": \"file:\\/\\///' | sed 's/\\/\",//' | xargs` \n\nSYRIUS_LIBRARIES=(\n \"$ZNN_SDK_DART_PATH/lib/src/argon2/blobs/libargon2_ffi.dylib\"\n \"$ZNN_SDK_DART_PATH/lib/src/pow/blobs/libpow_links.dylib\"\n)\n\necho \"$OUTPUT_DIRECTORY\"\n\nfor znn_library in ${SYRIUS_LIBRARIES[@]}; do\n echo \"Copy ZNN library: $znn_library\"\n cp $znn_library \"$OUTPUT_DIRECTORY\"\ndone\n"; }; + 96DA2500296EB70F00545E88 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 12; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "OUTPUT_DIRECTORY=\"./\"\n\nPACKAGE_CONFIG=\"../.dart_tool/package_config.json\"\nZNN_LEDGER_DART_PATH=`cat $PACKAGE_CONFIG | grep git/znn_ledger_dart | sed 's/\"rootUri\": \"file:\\/\\///' | sed 's/\\/\",//' | xargs` \n\nSYRIUS_LIBRARIES=(\n \"$ZNN_LEDGER_DART_PATH/lib/src/ledger/blobs/libledger_ffi.dylib\"\n)\n\necho \"$OUTPUT_DIRECTORY\"\n\nfor znn_library in ${SYRIUS_LIBRARIES[@]}; do\n echo \"Copy ZNN library: $znn_library\"\n cp $znn_library \"$OUTPUT_DIRECTORY\"\ndone\n"; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ diff --git a/pubspec.lock b/pubspec.lock index b9f0c1c6..c1cbe4e6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1464,6 +1464,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + version: + dependency: "direct main" + description: + name: version + sha256: "3d4140128e6ea10d83da32fef2fa4003fccbf6852217bb854845802f04191f94" + url: "https://pub.dev" + source: hosted + version: "3.0.2" wakelock_plus: dependency: "direct main" description: @@ -1584,15 +1592,24 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.2" + znn_ledger_dart: + dependency: "direct main" + description: + path: "." + ref: "feature/ledger" + resolved-ref: fe3069c758d51e17f02cc336315af6a7d5f51cb2 + url: "https://github.com/kinggorrin/znn_ledger_dart.git" + source: git + version: "0.0.2" znn_sdk_dart: dependency: "direct main" description: path: "." - ref: d960c0bdc6dc553eaa75d5ddddb34cab530578b8 - resolved-ref: d960c0bdc6dc553eaa75d5ddddb34cab530578b8 - url: "https://github.com/zenon-network/znn_sdk_dart.git" + ref: "feature/ledger" + resolved-ref: "51441ddbeaa634f51a2ea34e4325a3a01cc90e98" + url: "https://github.com/kinggorrin/znn_sdk_dart.git" source: git - version: "0.0.5" + version: "0.0.6" zxing2: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index df746ec1..4a577f66 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -43,8 +43,12 @@ dependencies: number_selector: ^1.0.5 znn_sdk_dart: git: - url: https://github.com/zenon-network/znn_sdk_dart.git - ref: d960c0bdc6dc553eaa75d5ddddb34cab530578b8 + url: https://github.com/kinggorrin/znn_sdk_dart.git + ref: feature/ledger + znn_ledger_dart: + git: + url: https://github.com/kinggorrin/znn_ledger_dart.git + ref: feature/ledger json_rpc_2: ^3.0.2 path: ^1.8.2 ffi: ^2.0.1 @@ -67,6 +71,7 @@ dependencies: wallet_connect_uri_validator: ^0.1.0 big_decimal: ^0.5.0 ai_barcode_scanner: ^0.0.7 + version: ^3.0.0 dev_dependencies: build_runner: ^2.1.7 hive_generator: ^2.0.0 diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 04781fcc..3313166a 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -106,10 +106,18 @@ string(REPLACE "\"rootUri\": \"file:///" "" ZNN_SDK_DART_PATH "${ZNN_SDK_DART_PA string(REPLACE "/\"," "" ZNN_SDK_DART_PATH "${ZNN_SDK_DART_PATH}") string(STRIP "${ZNN_SDK_DART_PATH}" ZNN_SDK_DART_PATH) +file(STRINGS "${SYRIUS_PROJECT_DIRECTORY}/.dart_tool/package_config.json" + ZNN_LEDGER_DART_PATH REGEX "(file:).*(znn_ledger_dart).*\\/\"" ) + +string(REPLACE "\"rootUri\": \"file:///" "" ZNN_LEDGER_DART_PATH "${ZNN_LEDGER_DART_PATH}") +string(REPLACE "/\"," "" ZNN_LEDGER_DART_PATH "${ZNN_LEDGER_DART_PATH}") +string(STRIP "${ZNN_LEDGER_DART_PATH}" ZNN_LEDGER_DART_PATH) + list(APPEND SYRIUS_LIBRARIES "${SYRIUS_PROJECT_DIRECTORY}/lib/embedded_node/blobs/libznn.dll" "${ZNN_SDK_DART_PATH}/lib/src/argon2/blobs/argon2_ffi_plugin.dll" "${ZNN_SDK_DART_PATH}/lib/src/pow/blobs/libpow_links.dll" + "${ZNN_LEDGER_DART_PATH}/lib/src/ledger/blobs/libledger_ffi.dll" ) foreach(znn_library ${SYRIUS_LIBRARIES})