diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero index 277d922c3..b9bc2dcc5 160000 --- a/crypto_plugins/flutter_libmonero +++ b/crypto_plugins/flutter_libmonero @@ -1 +1 @@ -Subproject commit 277d922c3b1d637c1ccda25f51395c618d293015 +Subproject commit b9bc2dcc56e13f235a6c5b0fc02c0e543eb87758 diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart index 2f325f536..9839c82f7 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart @@ -8,6 +8,7 @@ import 'package:bip39/src/wordlists/english.dart' as bip39wordlist; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_libmonero/monero/monero.dart'; +import 'package:flutter_libmonero/wownero/wownero.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart'; @@ -157,6 +158,11 @@ class _RestoreWalletViewState extends ConsumerState { var moneroWordList = monero.getMoneroWordList("English"); return moneroWordList.contains(word); } + if (widget.coin == Coin.wownero) { + var wowneroWordList = wownero.getWowneroWordList("English", + seedWordsLength: widget.seedWordsLength); + return wowneroWordList.contains(word); + } return _wordListHashSet.contains(word); } @@ -182,6 +188,8 @@ class _RestoreWalletViewState extends ConsumerState { if (widget.coin == Coin.monero) { height = monero.getHeigthByDate(date: widget.restoreFromDate); + } else if (widget.coin == Coin.wownero) { + height = wownero.getHeightByDate(date: widget.restoreFromDate); } // todo: wait until this implemented // else if (widget.coin == Coin.wownero) { diff --git a/lib/services/coins/monero/monero_wallet.dart b/lib/services/coins/monero/monero_wallet.dart index 69e0bd38d..91bb3c4db 100644 --- a/lib/services/coins/monero/monero_wallet.dart +++ b/lib/services/coins/monero/monero_wallet.dart @@ -699,7 +699,7 @@ class MoneroWallet extends CoinServiceAPI { name: name, type: WalletType.monero, isRecovery: false, - restoreHeight: credentials.height ?? 0, + restoreHeight: bufferedCreateHeight, date: DateTime.now(), path: path, dirPath: dirPath, diff --git a/lib/services/coins/wownero/wownero_wallet.dart b/lib/services/coins/wownero/wownero_wallet.dart index 7476afbef..c872c7d7e 100644 --- a/lib/services/coins/wownero/wownero_wallet.dart +++ b/lib/services/coins/wownero/wownero_wallet.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:io'; +import 'package:cw_core/get_height_by_date.dart'; import 'package:cw_core/monero_transaction_priority.dart'; import 'package:cw_core/node.dart'; import 'package:cw_core/pending_transaction.dart'; @@ -647,7 +648,7 @@ class WowneroWallet extends CoinServiceAPI { } //TODO: take in the default language when creating wallet. - Future _generateNewWallet() async { + Future _generateNewWallet({int seedWordsLength = 14}) async { Logging.instance .log("IS_INTEGRATION_TEST: $integrationTestFlag", level: LogLevel.Info); // TODO: ping wownero server and make sure the genesis hash matches @@ -685,9 +686,7 @@ class WowneroWallet extends CoinServiceAPI { await pathForWalletDir(name: name, type: WalletType.wownero); final path = await pathForWallet(name: name, type: WalletType.wownero); credentials = wownero.createWowneroNewWalletCredentials( - name: name, - language: "English", - ); + name: name, language: "English", seedWordsLength: seedWordsLength); walletInfo = WalletInfo.external( id: WalletBase.idFor(name, WalletType.wownero), @@ -712,9 +711,12 @@ class WowneroWallet extends CoinServiceAPI { // To restore from a seed final wallet = await _walletCreationService?.create(credentials); - // subtract a couple days to ensure we have a buffer for SWB - final bufferedCreateHeight = - getSeedHeightSync(wallet?.seed.trim() as String); + final bufferedCreateHeight = (seedWordsLength == 14) + ? getSeedHeightSync(wallet?.seed.trim() as String) + : wownero.getHeightByDate( + date: DateTime.now().subtract(const Duration( + days: + 2))); // subtract a couple days to ensure we have a buffer for SWB await DB.instance.put( boxName: walletId, key: "restoreHeight", value: bufferedCreateHeight); @@ -722,6 +724,7 @@ class WowneroWallet extends CoinServiceAPI { await _secureStore.write( key: '${_walletId}_mnemonic', value: wallet?.seed.trim()); + walletInfo.address = wallet?.walletAddresses.address; await DB.instance .add(boxName: WalletInfo.boxName, value: walletInfo); @@ -778,7 +781,7 @@ class WowneroWallet extends CoinServiceAPI { @override // TODO: implement initializeWallet - Future initializeNew() async { + Future initializeNew({int seedWordsLength = 14}) async { await _prefs.init(); // TODO: ping actual wownero network // try { @@ -796,7 +799,7 @@ class WowneroWallet extends CoinServiceAPI { prefs = await SharedPreferences.getInstance(); keysStorage = KeyService(storage!); - await _generateNewWallet(); + await _generateNewWallet(seedWordsLength: seedWordsLength); // var password; // try { // password = @@ -977,6 +980,14 @@ class WowneroWallet extends CoinServiceAPI { // extract seed height from 14 word seed if (seedLength == 14) { height = getSeedHeightSync(mnemonic.trim()); + } else { + // 25 word seed. TODO validate + if (height == 0) { + height = wownero.getHeightByDate( + date: DateTime.now().subtract(const Duration( + days: + 2))); // subtract a couple days to ensure we have a buffer for SWB\ + } } await DB.instance diff --git a/lib/utilities/constants.dart b/lib/utilities/constants.dart index e170dad2a..e27fbaa3d 100644 --- a/lib/utilities/constants.dart +++ b/lib/utilities/constants.dart @@ -62,9 +62,7 @@ abstract class Constants { values.addAll([25]); break; case Coin.wownero: - values.addAll([14]); - // todo: uncomment when wownero 25 word seeds implemented - // values.addAll([14, 25]); + values.addAll([14, 25]); break; } return values; diff --git a/test/services/coins/monero/monero_wallet_test.dart b/test/services/coins/monero/monero_wallet_test.dart new file mode 100644 index 000000000..d54959ab2 --- /dev/null +++ b/test/services/coins/monero/monero_wallet_test.dart @@ -0,0 +1,227 @@ +import 'dart:async'; +import 'dart:core'; +import 'dart:core' as core; +import 'dart:io'; +import 'dart:math'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:hive/hive.dart'; +import 'package:hive_test/hive_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'package:cw_core/monero_amount_format.dart'; +import 'package:cw_core/node.dart'; +import 'package:cw_core/pending_transaction.dart'; +import 'package:cw_core/unspent_coins_info.dart'; +import 'package:cw_core/wallet_base.dart'; +import 'package:cw_core/wallet_credentials.dart'; +import 'package:cw_core/wallet_info.dart'; +import 'package:cw_core/wallet_service.dart'; +import 'package:cw_core/wallet_type.dart'; +import 'package:cw_monero/api/wallet.dart'; +import 'package:cw_monero/api/wallet_manager.dart' as monero_wallet_manager; +import 'package:cw_monero/pending_monero_transaction.dart'; +import 'package:cw_monero/monero_wallet.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_libmonero/core/key_service.dart'; +import 'package:flutter_libmonero/core/wallet_creation_service.dart'; +import 'package:flutter_libmonero/view_model/send/output.dart'; +import 'package:flutter_libmonero/monero/monero.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:hive/hive.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'; + +import 'package:stackwallet/services/wallets.dart'; + +import 'dart:developer' as developer; + +// TODO trim down to the minimum imports above + +import 'monero_wallet_test_data.dart'; + +//FlutterSecureStorage? storage; +FakeSecureStorage? storage; +WalletService? walletService; +SharedPreferences? prefs; +KeyService? keysStorage; +MoneroWalletBase? walletBase; +late WalletCreationService _walletCreationService; +dynamic _walletInfoSource; +Wallets? walletsService; + +String path = ''; + +String name = 'namee${Random().nextInt(10000000)}'; +int nettype = 0; +WalletType type = WalletType.monero; + +@GenerateMocks([]) +void main() async { + storage = FakeSecureStorage(); + prefs = await SharedPreferences.getInstance(); + keysStorage = KeyService(storage!); + WalletInfo walletInfo = WalletInfo.external( + id: '', + name: '', + type: type, + isRecovery: false, + restoreHeight: 0, + date: DateTime.now(), + path: '', + address: '', + dirPath: ''); + late WalletCredentials credentials; + + WidgetsFlutterBinding.ensureInitialized(); + Directory appDir = (await getApplicationDocumentsDirectory()); + if (Platform.isIOS) { + appDir = (await getLibraryDirectory()); + } + await Hive.close(); + Hive.init(appDir.path); + Hive.registerAdapter(NodeAdapter()); + Hive.registerAdapter(WalletInfoAdapter()); + Hive.registerAdapter(WalletTypeAdapter()); + Hive.registerAdapter(UnspentCoinsInfoAdapter()); + + monero.onStartup(); + _walletInfoSource = await Hive.openBox(WalletInfo.boxName); + walletService = monero.createMoneroWalletService(_walletInfoSource); + + group("Mainnet tests", () { + setUp(() async { + try { + // if (name?.isEmpty ?? true) { + // name = await generateName(); + // } + final dirPath = await pathForWalletDir(name: name, type: type); + path = await pathForWallet(name: name, type: type); + credentials = + // // creating a new wallet + // monero.createMoneroNewWalletCredentials( + // name: name, language: "English"); + // restoring a previous wallet + monero.createMoneroRestoreWalletFromSeedCredentials( + name: name, height: 2580000, mnemonic: testMnemonic); + + walletInfo = WalletInfo.external( + id: WalletBase.idFor(name, type), + name: name, + type: type, + isRecovery: false, + restoreHeight: credentials.height ?? 0, + date: DateTime.now(), + path: path, + address: "", + dirPath: dirPath); + credentials.walletInfo = walletInfo; + + _walletCreationService = WalletCreationService( + secureStorage: storage, + sharedPreferences: prefs, + walletService: walletService, + keyService: keysStorage, + ); + _walletCreationService.changeWalletType(); + } catch (e, s) { + print(e); + print(s); + } + }); + + test("Test mainnet address generation from seed", () async { + final wallet = await + // _walletCreationService.create(credentials); + _walletCreationService.restoreFromSeed(credentials); + walletInfo.address = wallet.walletAddresses.address; + //print(walletInfo.address); + + await _walletInfoSource.add(walletInfo); + walletBase?.close(); + walletBase = wallet as MoneroWalletBase; + //print("${walletBase?.seed}"); + + // print(walletBase); + // loggerPrint(walletBase.toString()); + // loggerPrint("name: ${walletBase!.name} seed: ${walletBase!.seed} id: " + // "${walletBase!.id} walletinfo: ${toStringForinfo(walletBase!.walletInfo)} type: ${walletBase!.type} balance: " + // "${walletBase!.balance.entries.first.value.available} currency: ${walletBase!.currency}"); + + expect(walletInfo.address, mainnetTestData[0][0]); + expect( + await walletBase!.getTransactionAddress(0, 0), mainnetTestData[0][0]); + expect( + await walletBase!.getTransactionAddress(0, 1), mainnetTestData[0][1]); + expect( + await walletBase!.getTransactionAddress(0, 2), mainnetTestData[0][2]); + expect( + await walletBase!.getTransactionAddress(1, 0), mainnetTestData[1][0]); + expect( + await walletBase!.getTransactionAddress(1, 1), mainnetTestData[1][1]); + expect( + await walletBase!.getTransactionAddress(1, 2), mainnetTestData[1][2]); + }); + }); + /* + // Not needed; only folder created, wallet files not saved yet. TODO test saving and deleting wallet files and make sure to clean up leftover folder afterwards + group("Mainnet wallet deletion test", () { + test("Test mainnet wallet existence", () { + expect(monero_wallet_manager.isWalletExistSync(path: path), true); + }); + + test("Test mainnet wallet deletion", () { + // Remove wallet from wallet service + walletService?.remove(name); + walletsService?.removeWallet(walletId: name); + expect(monero_wallet_manager.isWalletExistSync(path: path), false); + }); + }); + + group("Mainnet node tests", () { + test("Test mainnet node connection", () async { + await walletBase?.connectToNode( + node: Node( + uri: "monero-stagenet.stackwallet.com:38081", + type: WalletType.moneroStageNet)); + await walletBase!.rescan( + height: + credentials.height); // Probably shouldn't be rescanning from 0... + await walletBase!.getNodeHeight(); + int height = await walletBase!.getNodeHeight(); + print('height: $height'); + bool connected = await walletBase!.isConnected(); + print('connected: $connected'); + + //expect... + }); + }); + */ + + // TODO test deletion of wallets ... and delete them +} + +Future pathForWalletDir( + {required String name, required WalletType type}) async { + Directory root = (await getApplicationDocumentsDirectory()); + if (Platform.isIOS) { + root = (await getLibraryDirectory()); + } + final prefix = walletTypeToString(type).toLowerCase(); + final walletsDir = Directory('${root.path}/wallets'); + final walletDire = Directory('${walletsDir.path}/$prefix/$name'); + + if (!walletDire.existsSync()) { + walletDire.createSync(recursive: true); + } + + return walletDire.path; +} + +Future pathForWallet( + {required String name, required WalletType type}) async => + await pathForWalletDir(name: name, type: type) + .then((path) => path + '/$name'); diff --git a/test/services/coins/monero/monero_wallet_test_data.dart b/test/services/coins/monero/monero_wallet_test_data.dart new file mode 100644 index 000000000..dc0a0f4cb --- /dev/null +++ b/test/services/coins/monero/monero_wallet_test_data.dart @@ -0,0 +1,14 @@ +String testMnemonic = + 'agreed aquarium wallets uptight karate wonders afoot guys itself nucleus reduce lamb fully fewest bimonthly dazed skulls magically mocked fugitive imbalance saga calamity dialect itself'; +var mainnetTestData = [ + [ + '4AeRgkWZsMJhAWKMeCZ3h4ZSPnAcW5VBtRFyLd6gBEf6GgJU2FHXDA6i1DnQTd6h8R3VU5AkbGcWSNhtSwNNPgaD48gp4nn', + '82WsoLmbZt3BPwJMF5PfT8GitThJzUq3FFoSQyr4fKfJdxZebgY3mHPcnAqTBA3FFwZRGxC4ZDwkfE1VVULPa55x3xXgCbj', + '84kYPuZ1eaVKGQhf26QPNWbSLQG16BywXdLYYShVrPNMLAUAWce5vcpRc78FxwRphrG6Cda7faCKdUMr8fUCH3peHPenvHy' + ], + [ + '86SF44CsTBYU3vk1X7nGBbQnrUSknGbd6Uw8a9hUUgy3KBeXTDvk3pm8upMzZKw17m3mLPEzbcPp5WLpYVoHR5PKNVtFrHH', + '8Aa9LNGdBHwYUMsy6M9ZVXMEkTBZyEDT7aQmY32trCxbU6dwkZJSCSbcpyL7UiTB9QXXosomZtJYvUJ296vTNX5yQ81KaA2', + '85C5zZRcaD89PKmXEwjcYMVAUqoH5rrAXe3GokvSupXnDmccYvZagz5Qem7bQLteEw4iFEJ9oRk9BNfjTi4K2cyTJbTMMPT' + ] +]; diff --git a/test/services/coins/wownero/wownero_wallet_test.dart b/test/services/coins/wownero/wownero_wallet_test.dart new file mode 100644 index 000000000..660bc1438 --- /dev/null +++ b/test/services/coins/wownero/wownero_wallet_test.dart @@ -0,0 +1,371 @@ +import 'dart:async'; +import 'dart:core'; +import 'dart:core' as core; +import 'dart:io'; +import 'dart:math'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:hive/hive.dart'; +import 'package:hive_test/hive_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'package:cw_core/monero_amount_format.dart'; +import 'package:cw_core/node.dart'; +import 'package:cw_core/pending_transaction.dart'; +import 'package:cw_core/unspent_coins_info.dart'; +import 'package:cw_core/wallet_base.dart'; +import 'package:cw_core/wallet_credentials.dart'; +import 'package:cw_core/wallet_info.dart'; +import 'package:cw_core/wallet_service.dart'; +import 'package:cw_core/wallet_type.dart'; +import 'package:cw_wownero/api/wallet.dart'; +import 'package:cw_wownero/pending_wownero_transaction.dart'; +import 'package:cw_wownero/wownero_wallet.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_libmonero/core/key_service.dart'; +import 'package:flutter_libmonero/core/wallet_creation_service.dart'; +import 'package:flutter_libmonero/view_model/send/output.dart'; +import 'package:flutter_libmonero/wownero/wownero.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'; +import 'package:hive/hive.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'wownero_wallet_test_data.dart'; + +FakeSecureStorage? storage; +WalletService? walletService; +SharedPreferences? prefs; +KeyService? keysStorage; +WowneroWalletBase? walletBase; +late WalletCreationService _walletCreationService; +dynamic _walletInfoSource; + +String path = ''; + +String name = ''; +int nettype = 0; +WalletType type = WalletType.wownero; + +@GenerateMocks([]) +void main() async { + storage = FakeSecureStorage(); + prefs = await SharedPreferences.getInstance(); + keysStorage = KeyService(storage!); + WalletInfo walletInfo = WalletInfo.external( + id: '', + name: '', + type: type, + isRecovery: false, + restoreHeight: 0, + date: DateTime.now(), + path: '', + address: '', + dirPath: ''); + late WalletCredentials credentials; + + WidgetsFlutterBinding.ensureInitialized(); + Directory appDir = (await getApplicationDocumentsDirectory()); + if (Platform.isIOS) { + appDir = (await getLibraryDirectory()); + } + await Hive.close(); + Hive.init(appDir.path); + Hive.registerAdapter(NodeAdapter()); + Hive.registerAdapter(WalletInfoAdapter()); + Hive.registerAdapter(WalletTypeAdapter()); + Hive.registerAdapter(UnspentCoinsInfoAdapter()); + + wownero.onStartup(); + _walletInfoSource = await Hive.openBox(WalletInfo.boxName); + walletService = wownero.createWowneroWalletService(_walletInfoSource); + + group("Wownero 14 word seed generation", () { + setUp(() async { + bool hasThrown = false; + try { + name = 'namee${Random().nextInt(10000000)}'; + final dirPath = await pathForWalletDir(name: name, type: type); + path = await pathForWallet(name: name, type: type); + credentials = wownero.createWowneroNewWalletCredentials( + name: name, + language: "English", + seedWordsLength: 14); // TODO catch failure + + walletInfo = WalletInfo.external( + id: WalletBase.idFor(name, type), + name: name, + type: type, + isRecovery: false, + restoreHeight: credentials.height ?? 0, + date: DateTime.now(), + path: path, + address: "", + dirPath: dirPath); + credentials.walletInfo = walletInfo; + + _walletCreationService = WalletCreationService( + secureStorage: storage, + sharedPreferences: prefs, + walletService: walletService, + keyService: keysStorage, + ); + _walletCreationService.changeWalletType(); + } catch (e, s) { + print(e); + print(s); + hasThrown = true; + } + expect(hasThrown, false); + }); + + test("Wownero 14 word seed address generation", () async { + final wallet = await _walletCreationService.create(credentials); + // TODO validate mnemonic + walletInfo.address = wallet.walletAddresses.address; + + bool hasThrown = false; + try { + await _walletInfoSource.add(walletInfo); + walletBase?.close(); + walletBase = wallet as WowneroWalletBase; + + // TODO validate + //expect(walletInfo.address, mainnetTestData14[0][0]); + } catch (_) { + hasThrown = true; + } + expect(hasThrown, false); + + walletBase?.close(); + walletBase = wallet as WowneroWalletBase; + }); + + // TODO delete left over wallet file with name: name + }); + + group("Wownero 14 word seed restoration", () { + setUp(() async { + bool hasThrown = false; + try { + name = 'namee${Random().nextInt(10000000)}'; + final dirPath = await pathForWalletDir(name: name, type: type); + path = await pathForWallet(name: name, type: type); + credentials = wownero.createWowneroRestoreWalletFromSeedCredentials( + name: name, + height: 465760, + mnemonic: testMnemonic14); // TODO catch failure + + walletInfo = WalletInfo.external( + id: WalletBase.idFor(name, type), + name: name, + type: type, + isRecovery: false, + restoreHeight: credentials.height ?? 0, + date: DateTime.now(), + path: path, + address: "", + dirPath: dirPath); + credentials.walletInfo = walletInfo; + + _walletCreationService = WalletCreationService( + secureStorage: storage, + sharedPreferences: prefs, + walletService: walletService, + keyService: keysStorage, + ); + _walletCreationService.changeWalletType(); + } catch (e, s) { + print(e); + print(s); + hasThrown = true; + } + expect(hasThrown, false); + }); + + test("Wownero 14 word seed address generation", () async { + final wallet = await _walletCreationService.restoreFromSeed(credentials); + walletInfo.address = wallet.walletAddresses.address; + + bool hasThrown = false; + try { + await _walletInfoSource.add(walletInfo); + walletBase?.close(); + walletBase = wallet as WowneroWalletBase; + + expect(walletInfo.address, mainnetTestData14[0][0]); + expect(await walletBase!.getTransactionAddress(0, 0), + mainnetTestData14[0][0]); + expect(await walletBase!.getTransactionAddress(0, 1), + mainnetTestData14[0][1]); + expect(await walletBase!.getTransactionAddress(0, 2), + mainnetTestData14[0][2]); + expect(await walletBase!.getTransactionAddress(1, 0), + mainnetTestData14[1][0]); + expect(await walletBase!.getTransactionAddress(1, 1), + mainnetTestData14[1][1]); + expect(await walletBase!.getTransactionAddress(1, 2), + mainnetTestData14[1][2]); + } catch (_) { + hasThrown = true; + } + expect(hasThrown, false); + + walletBase?.close(); + walletBase = wallet as WowneroWalletBase; + }); + + // TODO delete left over wallet file with name: name + }); + + group("Wownero 25 word seed generation", () { + setUp(() async { + bool hasThrown = false; + try { + name = 'namee${Random().nextInt(10000000)}'; + final dirPath = await pathForWalletDir(name: name, type: type); + path = await pathForWallet(name: name, type: type); + credentials = wownero.createWowneroNewWalletCredentials( + name: name, + language: "English", + seedWordsLength: 25); // TODO catch failure + + walletInfo = WalletInfo.external( + id: WalletBase.idFor(name, type), + name: name, + type: type, + isRecovery: false, + restoreHeight: credentials.height ?? 0, + date: DateTime.now(), + path: path, + address: "", + dirPath: dirPath); + credentials.walletInfo = walletInfo; + + _walletCreationService = WalletCreationService( + secureStorage: storage, + sharedPreferences: prefs, + walletService: walletService, + keyService: keysStorage, + ); + _walletCreationService.changeWalletType(); + } catch (e, s) { + print(e); + print(s); + hasThrown = true; + } + expect(hasThrown, false); + }); + + test("Wownero 25 word seed address generation", () async { + final wallet = await _walletCreationService.create(credentials); + // TODO validate mnemonic + walletInfo.address = wallet.walletAddresses.address; + + bool hasThrown = false; + try { + await _walletInfoSource.add(walletInfo); + walletBase?.close(); + walletBase = wallet as WowneroWalletBase; + + // TODO validate + //expect(walletInfo.address, mainnetTestData14[0][0]); + } catch (_) { + hasThrown = true; + } + expect(hasThrown, false); + + walletBase?.close(); + walletBase = wallet as WowneroWalletBase; + }); + + // TODO delete left over wallet file with name: name + }); + + group("Wownero 25 word seed restoration", () { + setUp(() async { + bool hasThrown = false; + try { + name = 'namee${Random().nextInt(10000000)}'; + final dirPath = await pathForWalletDir(name: name, type: type); + path = await pathForWallet(name: name, type: type); + credentials = wownero.createWowneroRestoreWalletFromSeedCredentials( + name: name, + height: 465760, + mnemonic: testMnemonic25); // TODO catch failure + + walletInfo = WalletInfo.external( + id: WalletBase.idFor(name, type), + name: name, + type: type, + isRecovery: false, + restoreHeight: credentials.height ?? 0, + date: DateTime.now(), + path: path, + address: "", + dirPath: dirPath); + credentials.walletInfo = walletInfo; + + _walletCreationService = WalletCreationService( + secureStorage: storage, + sharedPreferences: prefs, + walletService: walletService, + keyService: keysStorage, + ); + _walletCreationService.changeWalletType(); + } catch (e, s) { + print(e); + print(s); + hasThrown = true; + } + expect(hasThrown, false); + }); + + test("Wownero 25 word seed address generation", () async { + final wallet = await _walletCreationService.restoreFromSeed(credentials); + walletInfo.address = wallet.walletAddresses.address; + + bool hasThrown = false; + try { + await _walletInfoSource.add(walletInfo); + walletBase?.close(); + walletBase = wallet as WowneroWalletBase; + + expect(walletInfo.address, mainnetTestData25[0][0]); + } catch (_) { + hasThrown = true; + } + expect(hasThrown, false); + + walletBase?.close(); + walletBase = wallet as WowneroWalletBase; + }); + + // TODO delete left over wallet file with name: name + }); +} + +Future pathForWalletDir( + {required String name, required WalletType type}) async { + Directory root = (await getApplicationDocumentsDirectory()); + if (Platform.isIOS) { + root = (await getLibraryDirectory()); + } + final prefix = walletTypeToString(type).toLowerCase(); + final walletsDir = Directory('${root.path}/wallets'); + final walletDire = Directory('${walletsDir.path}/$prefix/$name'); + + if (!walletDire.existsSync()) { + walletDire.createSync(recursive: true); + } + + return walletDire.path; +} + +Future pathForWallet( + {required String name, required WalletType type}) async => + await pathForWalletDir(name: name, type: type) + .then((path) => path + '/$name'); diff --git a/test/services/coins/wownero/wownero_wallet_test_data.dart b/test/services/coins/wownero/wownero_wallet_test_data.dart new file mode 100644 index 000000000..7f27fc486 --- /dev/null +++ b/test/services/coins/wownero/wownero_wallet_test_data.dart @@ -0,0 +1,22 @@ +String testMnemonic14 = + 'weather cruise school such silly profit clerk wage reduce obtain ill sand episode shadow'; +var mainnetTestData14 = [ + [ + 'Wo3jmHvTMLwE6h29fpgcb8PbJSpaKuqM7XTXVfiiu8bLCZsJvrQCbQSJR48Vo3BWNQKsMsXZ4VixndXTH25QtorC27NCjmsEi', + 'WW3K54QzmMFB1uTZh3LVvgQYqANLmX1FkJHLJ4sU1E7BQmp8nGizyBnjNXSgsjCa4BQ3Rw3GG5jw1ByUkaUjSywm2KmHAbFvK', + 'WW3e3F51KAojcSW2G5WimmE1WVFsbBHc6HppZFBa6dNiEn21cThXzdGGDbpv89aTKXSRSPSFaetK6HgCozYawaYz2knUi9Hmn' + ], + [ + 'WW2nx7MFruyN2CcXnGnMbDdvqsyZUGQthLWKYPkQ4iM9XCE54RyWVjNjgopryUbyi9WKzYhHDai2wENbh1Jh1UHa28CL72TYt', + 'WW34p57QBMoD6MEZVTu5u9R7G3KeYqvN4eYbvHLYsgbWXpLe992fBvVB7ANJNvaGmPg2uwY5oKjwKbpo4fDU6cGS231PmvXrZ', + 'WW2KQLLt6gjC9gRsC4NGehbAZX6UPU7sK89UQFwSg3NKj3MXPwnjh5BiJVqYYNQb6JNsfa7oP7eDjLagtLa2H6YP11RhUNQqw' + ] +]; + +String testMnemonic25 = + 'myth byline benches sadness nylon tamper guide giving match angled lurk rally makeup alarms river soapy dolphin woven ticket maul examine public luggage mammal alarms'; +var mainnetTestData25 = [ + [ + 'Wo3piMnt1ztjLktFJNsfs9ce6N1tyHk7DB93cNqTGPJ7To3RS7W2q5DdxgQAG5E6RQXQhchQD7ip8WWL3fD8Ww5K2XmAXYxta' + ] +];