From 289f0b89597d7f5df29ae4b082d7684945c185ec Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 13 Oct 2022 22:13:42 -0500 Subject: [PATCH 01/17] Add mainnet tests for address generation from test mnemonic TODO: Clean up the wallet file as the tests conclude --- .../coins/monero/monero_wallet_test.dart | 207 ++++++++++++++++++ .../coins/monero/monero_wallet_test_data.dart | 14 ++ 2 files changed, 221 insertions(+) create mode 100644 test/services/coins/monero/monero_wallet_test.dart create mode 100644 test/services/coins/monero/monero_wallet_test_data.dart 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..1ee2fc44a --- /dev/null +++ b/test/services/coins/monero/monero_wallet_test.dart @@ -0,0 +1,207 @@ +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/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 '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; + +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); + final 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]); + }); + }); + /* + 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' + ] +]; From a3a36d02c3a7e6f4ef6b166a5afd6cad55012a25 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 14 Oct 2022 10:09:17 -0500 Subject: [PATCH 02/17] Import wallets service and remove wallet at conclusion of tests --- .../coins/monero/monero_wallet_test.dart | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/services/coins/monero/monero_wallet_test.dart b/test/services/coins/monero/monero_wallet_test.dart index 1ee2fc44a..5b802a089 100644 --- a/test/services/coins/monero/monero_wallet_test.dart +++ b/test/services/coins/monero/monero_wallet_test.dart @@ -34,6 +34,8 @@ 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 @@ -48,6 +50,7 @@ KeyService? keysStorage; MoneroWalletBase? walletBase; late WalletCreationService _walletCreationService; dynamic _walletInfoSource; +Wallets? walletsService; String name = 'namee${Random().nextInt(10000000)}'; int nettype = 0; @@ -160,6 +163,22 @@ void main() async { await walletBase!.getTransactionAddress(1, 2), mainnetTestData[1][2]); }); }); + + group("Mainnet wallet deletion test", () { + test("Test mainnet wallet deletion", () async { + // Remove wallet from wallet service + walletService?.remove(name); + walletsService?.removeWallet(walletId: name); + + // TODO test deletion, get code from generation for checking if it already exists + }); + + /* + // wait for widget tree to dispose of any widgets watching the manager + await Future.delayed(const Duration(seconds: 1)); + walletsInstance.removeWallet(walletId: walletId); + */ + }); /* group("Mainnet node tests", () { test("Test mainnet node connection", () async { From b6e51cb954ff1b90765b582487ecb3141fc5c840 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 14 Oct 2022 10:24:05 -0500 Subject: [PATCH 03/17] Add template wallet detection and removal code Not enabled as wallet files are not currently being saved; only an empty folder is created and left. TODO clean that up --- .../coins/monero/monero_wallet_test.dart | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/test/services/coins/monero/monero_wallet_test.dart b/test/services/coins/monero/monero_wallet_test.dart index 5b802a089..d54959ab2 100644 --- a/test/services/coins/monero/monero_wallet_test.dart +++ b/test/services/coins/monero/monero_wallet_test.dart @@ -20,6 +20,7 @@ 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'; @@ -52,6 +53,8 @@ late WalletCreationService _walletCreationService; dynamic _walletInfoSource; Wallets? walletsService; +String path = ''; + String name = 'namee${Random().nextInt(10000000)}'; int nettype = 0; WalletType type = WalletType.monero; @@ -96,7 +99,7 @@ void main() async { // name = await generateName(); // } final dirPath = await pathForWalletDir(name: name, type: type); - final path = await pathForWallet(name: name, type: type); + path = await pathForWallet(name: name, type: type); credentials = // // creating a new wallet // monero.createMoneroNewWalletCredentials( @@ -163,23 +166,21 @@ void main() async { 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 deletion", () async { + 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); - - // TODO test deletion, get code from generation for checking if it already exists + expect(monero_wallet_manager.isWalletExistSync(path: path), false); }); - - /* - // wait for widget tree to dispose of any widgets watching the manager - await Future.delayed(const Duration(seconds: 1)); - walletsInstance.removeWallet(walletId: walletId); - */ }); - /* + group("Mainnet node tests", () { test("Test mainnet node connection", () async { await walletBase?.connectToNode( From c2aeb5bae831eaa6baa964b66d9a821063889c80 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 14 Oct 2022 18:38:46 -0500 Subject: [PATCH 04/17] Add Wownero mainnet wallet address generation test --- .../coins/wownero/wownero_wallet_test.dart | 164 ++++++++++++++++++ .../wownero/wownero_wallet_test_data.dart | 14 ++ 2 files changed, 178 insertions(+) create mode 100644 test/services/coins/wownero/wownero_wallet_test.dart create mode 100644 test/services/coins/wownero/wownero_wallet_test_data.dart 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..e64dd772c --- /dev/null +++ b/test/services/coins/wownero/wownero_wallet_test.dart @@ -0,0 +1,164 @@ +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 = 'namee${Random().nextInt(10000000)}'; +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 tests", () { + setUp(() async { + try { + final dirPath = await pathForWalletDir(name: name, type: type); + path = await pathForWallet(name: name, type: type); + credentials = wownero.createWowneroRestoreWalletFromSeedCredentials( + name: name, height: 465760, 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.restoreFromSeed(credentials); + walletInfo.address = wallet.walletAddresses.address; + + await _walletInfoSource.add(walletInfo); + walletBase?.close(); + walletBase = wallet as WowneroWalletBase; + + 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]); + }); + }); +} + +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..b0d93a448 --- /dev/null +++ b/test/services/coins/wownero/wownero_wallet_test_data.dart @@ -0,0 +1,14 @@ +String testMnemonic = + 'weather cruise school such silly profit clerk wage reduce obtain ill sand episode shadow'; +var mainnetTestData = [ + [ + 'Wo3jmHvTMLwE6h29fpgcb8PbJSpaKuqM7XTXVfiiu8bLCZsJvrQCbQSJR48Vo3BWNQKsMsXZ4VixndXTH25QtorC27NCjmsEi', + 'WW3K54QzmMFB1uTZh3LVvgQYqANLmX1FkJHLJ4sU1E7BQmp8nGizyBnjNXSgsjCa4BQ3Rw3GG5jw1ByUkaUjSywm2KmHAbFvK', + 'WW3e3F51KAojcSW2G5WimmE1WVFsbBHc6HppZFBa6dNiEn21cThXzdGGDbpv89aTKXSRSPSFaetK6HgCozYawaYz2knUi9Hmn' + ], + [ + 'WW2nx7MFruyN2CcXnGnMbDdvqsyZUGQthLWKYPkQ4iM9XCE54RyWVjNjgopryUbyi9WKzYhHDai2wENbh1Jh1UHa28CL72TYt', + 'WW34p57QBMoD6MEZVTu5u9R7G3KeYqvN4eYbvHLYsgbWXpLe992fBvVB7ANJNvaGmPg2uwY5oKjwKbpo4fDU6cGS231PmvXrZ', + 'WW2KQLLt6gjC9gRsC4NGehbAZX6UPU7sK89UQFwSg3NKj3MXPwnjh5BiJVqYYNQb6JNsfa7oP7eDjLagtLa2H6YP11RhUNQqw' + ] +]; From 6223df54320afea50d258580d4780b820491d35d Mon Sep 17 00:00:00 2001 From: sneurlax Date: Mon, 7 Nov 2022 11:21:10 -0600 Subject: [PATCH 05/17] specify tests for 14 word seeds and add more error checking code to test and update ref to flutter_libmonero enabling tests (mocked storage, etc) --- crypto_plugins/flutter_libmonero | 2 +- .../coins/wownero/wownero_wallet_test.dart | 45 +++++++++++-------- .../wownero/wownero_wallet_test_data.dart | 4 +- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero index 277d922c3..d92fea3e6 160000 --- a/crypto_plugins/flutter_libmonero +++ b/crypto_plugins/flutter_libmonero @@ -1 +1 @@ -Subproject commit 277d922c3b1d637c1ccda25f51395c618d293015 +Subproject commit d92fea3e6b915b79697deafbd5710fb4c15bfc76 diff --git a/test/services/coins/wownero/wownero_wallet_test.dart b/test/services/coins/wownero/wownero_wallet_test.dart index e64dd772c..83b4844c7 100644 --- a/test/services/coins/wownero/wownero_wallet_test.dart +++ b/test/services/coins/wownero/wownero_wallet_test.dart @@ -83,13 +83,13 @@ void main() async { _walletInfoSource = await Hive.openBox(WalletInfo.boxName); walletService = wownero.createWowneroWalletService(_walletInfoSource); - group("Wownero tests", () { + group("Wownero 14 word tests", () { setUp(() async { try { final dirPath = await pathForWalletDir(name: name, type: type); path = await pathForWallet(name: name, type: type); credentials = wownero.createWowneroRestoreWalletFromSeedCredentials( - name: name, height: 465760, mnemonic: testMnemonic); + name: name, height: 465760, mnemonic: testMnemonic14); walletInfo = WalletInfo.external( id: WalletBase.idFor(name, type), @@ -116,27 +116,36 @@ void main() async { } }); - test("Test mainnet address generation from seed", () async { + test("Test mainnet address generation from 14 word seed", () async { final wallet = await _walletCreationService.restoreFromSeed(credentials); walletInfo.address = wallet.walletAddresses.address; - await _walletInfoSource.add(walletInfo); + 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; - - 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]); }); }); } diff --git a/test/services/coins/wownero/wownero_wallet_test_data.dart b/test/services/coins/wownero/wownero_wallet_test_data.dart index b0d93a448..8ab825dfa 100644 --- a/test/services/coins/wownero/wownero_wallet_test_data.dart +++ b/test/services/coins/wownero/wownero_wallet_test_data.dart @@ -1,6 +1,6 @@ -String testMnemonic = +String testMnemonic14 = 'weather cruise school such silly profit clerk wage reduce obtain ill sand episode shadow'; -var mainnetTestData = [ +var mainnetTestData14 = [ [ 'Wo3jmHvTMLwE6h29fpgcb8PbJSpaKuqM7XTXVfiiu8bLCZsJvrQCbQSJR48Vo3BWNQKsMsXZ4VixndXTH25QtorC27NCjmsEi', 'WW3K54QzmMFB1uTZh3LVvgQYqANLmX1FkJHLJ4sU1E7BQmp8nGizyBnjNXSgsjCa4BQ3Rw3GG5jw1ByUkaUjSywm2KmHAbFvK', From cb83515dbc774abc9c9e49fbd9f18f265ec699a1 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Mon, 7 Nov 2022 13:45:25 -0600 Subject: [PATCH 06/17] do not use wownero-seed (wow_seed) --- crypto_plugins/flutter_libmonero | 2 +- .../coins/wownero/wownero_wallet_test.dart | 107 ++++++++++++++++++ .../wownero/wownero_wallet_test_data.dart | 8 ++ 3 files changed, 116 insertions(+), 1 deletion(-) diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero index d92fea3e6..371443607 160000 --- a/crypto_plugins/flutter_libmonero +++ b/crypto_plugins/flutter_libmonero @@ -1 +1 @@ -Subproject commit d92fea3e6b915b79697deafbd5710fb4c15bfc76 +Subproject commit 371443607433a0ec509e2933ee21def29e9ad429 diff --git a/test/services/coins/wownero/wownero_wallet_test.dart b/test/services/coins/wownero/wownero_wallet_test.dart index 83b4844c7..945d8650f 100644 --- a/test/services/coins/wownero/wownero_wallet_test.dart +++ b/test/services/coins/wownero/wownero_wallet_test.dart @@ -83,8 +83,10 @@ void main() async { _walletInfoSource = await Hive.openBox(WalletInfo.boxName); walletService = wownero.createWowneroWalletService(_walletInfoSource); + /* group("Wownero 14 word tests", () { setUp(() async { + bool hasThrown = false; try { final dirPath = await pathForWalletDir(name: name, type: type); path = await pathForWallet(name: name, type: type); @@ -113,7 +115,9 @@ void main() async { } catch (e, s) { print(e); print(s); + hasThrown = true; } + expect(hasThrown, false); }); test("Test mainnet address generation from 14 word seed", () async { @@ -147,6 +151,109 @@ void main() async { walletBase?.close(); walletBase = wallet as WowneroWalletBase; }); + + // TODO delete left over wallet file with name: name + }); + */ + + group("Wownero 25 word tests", () { + setUp(() async { + bool hasThrown = false; + try { + final dirPath = await pathForWalletDir(name: name, type: type); + path = await pathForWallet(name: name, type: type); + credentials = wownero.createWowneroRestoreWalletFromSeedCredentials( + name: name, height: 465760, mnemonic: testMnemonic25); + + 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("Test mainnet address generation from 25 word seed", () 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); + try { + credentials = wownero.createWowneroRestoreWalletFromSeedCredentials( + name: name, height: 465760, mnemonic: testMnemonic25); + } catch (e, s) { + print(e); + print(s); + hasThrown = true; + } + expect(hasThrown, false); + + 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); + + final wallet = await _walletCreationService.restoreFromSeed(credentials); + walletInfo.address = wallet.walletAddresses.address; + + 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 }); } diff --git a/test/services/coins/wownero/wownero_wallet_test_data.dart b/test/services/coins/wownero/wownero_wallet_test_data.dart index 8ab825dfa..7f27fc486 100644 --- a/test/services/coins/wownero/wownero_wallet_test_data.dart +++ b/test/services/coins/wownero/wownero_wallet_test_data.dart @@ -12,3 +12,11 @@ var mainnetTestData14 = [ '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' + ] +]; From 6e5a0bad784831ba7c98db99b3291f6ab305ac48 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Mon, 7 Nov 2022 14:42:52 -0600 Subject: [PATCH 07/17] do not use wownero-seed (wow_seed) function for height, hardcoded POC --- crypto_plugins/flutter_libmonero | 2 +- lib/services/coins/wownero/wownero_wallet.dart | 7 ++++--- .../services/coins/wownero/wownero_wallet_test.dart | 13 ++----------- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero index 371443607..da826f313 160000 --- a/crypto_plugins/flutter_libmonero +++ b/crypto_plugins/flutter_libmonero @@ -1 +1 @@ -Subproject commit 371443607433a0ec509e2933ee21def29e9ad429 +Subproject commit da826f31352c695942bc9b821d1d0c82a9267ade diff --git a/lib/services/coins/wownero/wownero_wallet.dart b/lib/services/coins/wownero/wownero_wallet.dart index d3aba5bbb..a069414b6 100644 --- a/lib/services/coins/wownero/wownero_wallet.dart +++ b/lib/services/coins/wownero/wownero_wallet.dart @@ -713,8 +713,8 @@ class WowneroWallet extends CoinServiceAPI { 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 = 0; + //final bufferedCreateHeight = getSeedHeightSync(wallet?.seed.trim() as String); // TODO use an alternative to wow_seed's get_seed_height await DB.instance.put( boxName: walletId, key: "restoreHeight", value: bufferedCreateHeight); @@ -969,7 +969,8 @@ class WowneroWallet extends CoinServiceAPI { await _secureStore.write( key: '${_walletId}_mnemonic', value: mnemonic.trim()); - height = getSeedHeightSync(mnemonic.trim()); + height = 0; + //height = getSeedHeightSync(mnemonic.trim()); // TODO use an alternative to wow_seed's get_seed_height await DB.instance .put(boxName: walletId, key: "restoreHeight", value: height); diff --git a/test/services/coins/wownero/wownero_wallet_test.dart b/test/services/coins/wownero/wownero_wallet_test.dart index 945d8650f..ca7540da5 100644 --- a/test/services/coins/wownero/wownero_wallet_test.dart +++ b/test/services/coins/wownero/wownero_wallet_test.dart @@ -91,7 +91,7 @@ void main() async { final dirPath = await pathForWalletDir(name: name, type: type); path = await pathForWallet(name: name, type: type); credentials = wownero.createWowneroRestoreWalletFromSeedCredentials( - name: name, height: 465760, mnemonic: testMnemonic14); + name: name, height: 465760, mnemonic: testMnemonic14); // TODO catch failure walletInfo = WalletInfo.external( id: WalletBase.idFor(name, type), @@ -163,7 +163,7 @@ void main() async { final dirPath = await pathForWalletDir(name: name, type: type); path = await pathForWallet(name: name, type: type); credentials = wownero.createWowneroRestoreWalletFromSeedCredentials( - name: name, height: 465760, mnemonic: testMnemonic25); + name: name, height: 465760, mnemonic: testMnemonic25); // TODO catch failure walletInfo = WalletInfo.external( id: WalletBase.idFor(name, type), @@ -198,15 +198,6 @@ void main() async { name = 'namee${Random().nextInt(10000000)}'; final dirPath = await pathForWalletDir(name: name, type: type); path = await pathForWallet(name: name, type: type); - try { - credentials = wownero.createWowneroRestoreWalletFromSeedCredentials( - name: name, height: 465760, mnemonic: testMnemonic25); - } catch (e, s) { - print(e); - print(s); - hasThrown = true; - } - expect(hasThrown, false); walletInfo = WalletInfo.external( id: WalletBase.idFor(name, type), From b41c4c37bd6c5756fe8be0f4011da685d1265bd8 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Mon, 7 Nov 2022 14:46:48 -0600 Subject: [PATCH 08/17] delineate divergence point more clearly --- lib/services/coins/wownero/wownero_wallet.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/services/coins/wownero/wownero_wallet.dart b/lib/services/coins/wownero/wownero_wallet.dart index a069414b6..1153a881a 100644 --- a/lib/services/coins/wownero/wownero_wallet.dart +++ b/lib/services/coins/wownero/wownero_wallet.dart @@ -713,8 +713,12 @@ class WowneroWallet extends CoinServiceAPI { final wallet = await _walletCreationService?.create(credentials); // subtract a couple days to ensure we have a buffer for SWB + // 14 words + //final bufferedCreateHeight = getSeedHeightSync(wallet?.seed.trim() as String); + + // 25 words final bufferedCreateHeight = 0; - //final bufferedCreateHeight = getSeedHeightSync(wallet?.seed.trim() as String); // TODO use an alternative to wow_seed's get_seed_height + // TODO use an alternative to wow_seed's get_seed_height await DB.instance.put( boxName: walletId, key: "restoreHeight", value: bufferedCreateHeight); From 77aa3bc8e484534f893c3ee5f04820bca50918ad Mon Sep 17 00:00:00 2001 From: sneurlax Date: Mon, 7 Nov 2022 16:45:26 -0600 Subject: [PATCH 09/17] use wownero-seed for 14 word seed, use wownero wallet2 for 25 word seed and update tests showing examples of both. TODO proper validation, must eg calculate and check checksums etc --- crypto_plugins/flutter_libmonero | 2 +- .../coins/wownero/wownero_wallet_test.dart | 40 ++++++++++--------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero index da826f313..e95c19662 160000 --- a/crypto_plugins/flutter_libmonero +++ b/crypto_plugins/flutter_libmonero @@ -1 +1 @@ -Subproject commit da826f31352c695942bc9b821d1d0c82a9267ade +Subproject commit e95c19662ccf17d83109ab7b651cfbc0521deb47 diff --git a/test/services/coins/wownero/wownero_wallet_test.dart b/test/services/coins/wownero/wownero_wallet_test.dart index ca7540da5..2099df364 100644 --- a/test/services/coins/wownero/wownero_wallet_test.dart +++ b/test/services/coins/wownero/wownero_wallet_test.dart @@ -83,7 +83,6 @@ void main() async { _walletInfoSource = await Hive.openBox(WalletInfo.boxName); walletService = wownero.createWowneroWalletService(_walletInfoSource); - /* group("Wownero 14 word tests", () { setUp(() async { bool hasThrown = false; @@ -91,7 +90,9 @@ void main() async { 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 + name: name, + height: 465760, + mnemonic: testMnemonic14); // TODO catch failure walletInfo = WalletInfo.external( id: WalletBase.idFor(name, type), @@ -128,21 +129,21 @@ void main() async { try { await _walletInfoSource.add(walletInfo); walletBase?.close(); - walletBase = wallet as WowneroWalletBase; + 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]); + 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; } @@ -151,19 +152,21 @@ void main() async { walletBase?.close(); walletBase = wallet as WowneroWalletBase; }); - + // TODO delete left over wallet file with name: name }); - */ group("Wownero 25 word tests", () { 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 + name: name, + height: 465760, + mnemonic: testMnemonic25); // TODO catch failure walletInfo = WalletInfo.external( id: WalletBase.idFor(name, type), @@ -195,7 +198,6 @@ void main() async { test("Test mainnet address generation from 25 word seed", () 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); From 7c3d40782cbd024911e4626f6a0aa15d85290b79 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 8 Nov 2022 09:55:15 -0600 Subject: [PATCH 10/17] add generation tests and update flutter_libmonero ref change seedWords to SeedWordsLength to match rest of codebase --- crypto_plugins/flutter_libmonero | 2 +- .../coins/wownero/wownero_wallet.dart | 20 +- .../coins/wownero/wownero_wallet_test.dart | 172 ++++++++++++++---- 3 files changed, 147 insertions(+), 47 deletions(-) diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero index e95c19662..afdee4b88 160000 --- a/crypto_plugins/flutter_libmonero +++ b/crypto_plugins/flutter_libmonero @@ -1 +1 @@ -Subproject commit e95c19662ccf17d83109ab7b651cfbc0521deb47 +Subproject commit afdee4b880202f39a2375afc320f0642e98a1827 diff --git a/lib/services/coins/wownero/wownero_wallet.dart b/lib/services/coins/wownero/wownero_wallet.dart index 1153a881a..a0dc7bfe0 100644 --- a/lib/services/coins/wownero/wownero_wallet.dart +++ b/lib/services/coins/wownero/wownero_wallet.dart @@ -647,7 +647,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 @@ -687,6 +687,7 @@ class WowneroWallet extends CoinServiceAPI { credentials = wownero.createWowneroNewWalletCredentials( name: name, language: "English", + seedWordsLength: seedWordsLength ); walletInfo = WalletInfo.external( @@ -713,12 +714,12 @@ class WowneroWallet extends CoinServiceAPI { final wallet = await _walletCreationService?.create(credentials); // subtract a couple days to ensure we have a buffer for SWB - // 14 words - //final bufferedCreateHeight = getSeedHeightSync(wallet?.seed.trim() as String); - - // 25 words - final bufferedCreateHeight = 0; - // TODO use an alternative to wow_seed's get_seed_height + if (seedWordsLength == 14) { + final bufferedCreateHeight = getSeedHeightSync(wallet?.seed.trim() as String); + } else { + final bufferedCreateHeight = 0; + // TODO use an alternative to wow_seed's get_seed_height + } await DB.instance.put( boxName: walletId, key: "restoreHeight", value: bufferedCreateHeight); @@ -726,6 +727,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); @@ -782,7 +784,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 { @@ -800,7 +802,7 @@ class WowneroWallet extends CoinServiceAPI { prefs = await SharedPreferences.getInstance(); keysStorage = KeyService(storage!); - await _generateNewWallet(); + await _generateNewWallet(seedWordsLength: seedWordsLength); // var password; // try { // password = diff --git a/test/services/coins/wownero/wownero_wallet_test.dart b/test/services/coins/wownero/wownero_wallet_test.dart index 2099df364..660bc1438 100644 --- a/test/services/coins/wownero/wownero_wallet_test.dart +++ b/test/services/coins/wownero/wownero_wallet_test.dart @@ -46,7 +46,7 @@ dynamic _walletInfoSource; String path = ''; -String name = 'namee${Random().nextInt(10000000)}'; +String name = ''; int nettype = 0; WalletType type = WalletType.wownero; @@ -83,10 +83,75 @@ void main() async { _walletInfoSource = await Hive.openBox(WalletInfo.boxName); walletService = wownero.createWowneroWalletService(_walletInfoSource); - group("Wownero 14 word tests", () { + 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( @@ -121,7 +186,7 @@ void main() async { expect(hasThrown, false); }); - test("Test mainnet address generation from 14 word seed", () async { + test("Wownero 14 word seed address generation", () async { final wallet = await _walletCreationService.restoreFromSeed(credentials); walletInfo.address = wallet.walletAddresses.address; @@ -156,7 +221,71 @@ void main() async { // TODO delete left over wallet file with name: name }); - group("Wownero 25 word tests", () { + 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 { @@ -195,42 +324,11 @@ void main() async { expect(hasThrown, false); }); - test("Test mainnet address generation from 25 word seed", () async { - bool hasThrown = false; - try { - final dirPath = await pathForWalletDir(name: name, type: type); - path = await pathForWallet(name: name, type: type); - - 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; - hasThrown = false; + bool hasThrown = false; try { await _walletInfoSource.add(walletInfo); walletBase?.close(); From 48e8501e27c8f2e8494c2746710c18be276dbce2 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 8 Nov 2022 13:35:27 -0600 Subject: [PATCH 11/17] cherrypick e41f8088b02c8aeaac8caaebac27dcbe7cc1d893 --- .../restore_options_view.dart | 36 +++++++++++++++---- .../restore_wallet_view.dart | 5 +++ .../coins/wownero/wownero_wallet.dart | 11 ++++-- lib/utilities/constants.dart | 6 +--- 4 files changed, 45 insertions(+), 13 deletions(-) diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart index 76e74fa14..1ce5d713a 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart @@ -252,7 +252,11 @@ class _RestoreOptionsViewState extends ConsumerState { SizedBox( height: isDesktop ? 40 : 24, ), - if (coin == Coin.monero || coin == Coin.epicCash) + if (coin == Coin.monero || + coin == Coin.epicCash || + (coin == Coin.wownero && + ref.watch(mnemonicWordCountStateProvider.state).state == + 25)) Text( "Choose start date", style: isDesktop @@ -264,11 +268,19 @@ class _RestoreOptionsViewState extends ConsumerState { : STextStyles.smallMed12(context), textAlign: TextAlign.left, ), - if (coin == Coin.monero || coin == Coin.epicCash) + if (coin == Coin.monero || + coin == Coin.epicCash || + (coin == Coin.wownero && + ref.watch(mnemonicWordCountStateProvider.state).state == + 25)) SizedBox( height: isDesktop ? 16 : 8, ), - if (coin == Coin.monero || coin == Coin.epicCash) + if (coin == Coin.monero || + coin == Coin.epicCash || + (coin == Coin.wownero && + ref.watch(mnemonicWordCountStateProvider.state).state == + 25)) // if (!isDesktop) RestoreFromDatePicker( @@ -278,11 +290,19 @@ class _RestoreOptionsViewState extends ConsumerState { // if (isDesktop) // // TODO desktop date picker - if (coin == Coin.monero || coin == Coin.epicCash) + if (coin == Coin.monero || + coin == Coin.epicCash || + (coin == Coin.wownero && + ref.watch(mnemonicWordCountStateProvider.state).state == + 25)) const SizedBox( height: 8, ), - if (coin == Coin.monero || coin == Coin.epicCash) + if (coin == Coin.monero || + coin == Coin.epicCash || + (coin == Coin.wownero && + ref.watch(mnemonicWordCountStateProvider.state).state == + 25)) RoundedWhiteContainer( child: Center( child: Text( @@ -299,7 +319,11 @@ class _RestoreOptionsViewState extends ConsumerState { ), ), ), - if (coin == Coin.monero || coin == Coin.epicCash) + if (coin == Coin.monero || + coin == Coin.epicCash || + (coin == Coin.wownero && + ref.watch(mnemonicWordCountStateProvider.state).state == + 25)) SizedBox( height: isDesktop ? 24 : 16, ), 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 def0724b5..a6b7e7e77 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 @@ -149,6 +149,7 @@ class _RestoreWalletViewState extends ConsumerState { super.dispose(); } + // TODO: check for wownero wordlist? bool _isValidMnemonicWord(String word) { // TODO: get the actual language if (widget.coin == Coin.monero) { @@ -181,6 +182,10 @@ class _RestoreWalletViewState extends ConsumerState { if (widget.coin == Coin.monero) { height = monero.getHeigthByDate(date: widget.restoreFromDate); } + // todo: wait until this implemented + // else if (widget.coin == Coin.wownero) { + // height = wownero.getHeightByDate(date: widget.restoreFromDate); + // } // TODO: make more robust estimate of date maybe using https://explorer.epic.tech/api-index if (widget.coin == Coin.epicCash) { diff --git a/lib/services/coins/wownero/wownero_wallet.dart b/lib/services/coins/wownero/wownero_wallet.dart index a0dc7bfe0..5c419afd1 100644 --- a/lib/services/coins/wownero/wownero_wallet.dart +++ b/lib/services/coins/wownero/wownero_wallet.dart @@ -948,6 +948,11 @@ class WowneroWallet extends CoinServiceAPI { required int maxNumberOfIndexesToCheck, required int height, }) async { + final int seedLength = mnemonic.trim().split(" ").length; + if (!(seedLength == 14 || seedLength == 25)) { + throw Exception("Invalid wownero mnemonic length found: $seedLength"); + } + await _prefs.init(); longMutex = true; final start = DateTime.now(); @@ -975,8 +980,10 @@ class WowneroWallet extends CoinServiceAPI { await _secureStore.write( key: '${_walletId}_mnemonic', value: mnemonic.trim()); - height = 0; - //height = getSeedHeightSync(mnemonic.trim()); // TODO use an alternative to wow_seed's get_seed_height + // extract seed height from 14 word seed + if (seedLength == 14) { + height = getSeedHeightSync(mnemonic.trim()); + } await DB.instance .put(boxName: walletId, key: "restoreHeight", value: height); diff --git a/lib/utilities/constants.dart b/lib/utilities/constants.dart index 4fb3fb54b..e27fbaa3d 100644 --- a/lib/utilities/constants.dart +++ b/lib/utilities/constants.dart @@ -35,10 +35,6 @@ abstract class Constants { static const int pinLength = 4; - // enable testnet - // TODO: currently unused - static const bool allowTestnets = true; - // Enable Logger.print statements static const bool disableLogger = false; @@ -66,7 +62,7 @@ abstract class Constants { values.addAll([25]); break; case Coin.wownero: - values.addAll([14]); + values.addAll([14, 25]); break; } return values; From d23f6f2823a2d53b578f930c11fb4b41e0b25a27 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 8 Nov 2022 13:48:29 -0600 Subject: [PATCH 12/17] return to use of final for bufferedCreateHeight using inline if and use wowlet's height estimation function for 14 word seeds --- crypto_plugins/flutter_libmonero | 2 +- lib/services/coins/wownero/wownero_wallet.dart | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero index afdee4b88..2d5f5e563 160000 --- a/crypto_plugins/flutter_libmonero +++ b/crypto_plugins/flutter_libmonero @@ -1 +1 @@ -Subproject commit afdee4b880202f39a2375afc320f0642e98a1827 +Subproject commit 2d5f5e5636bdc4b211b2236492268167b5b969d0 diff --git a/lib/services/coins/wownero/wownero_wallet.dart b/lib/services/coins/wownero/wownero_wallet.dart index 5c419afd1..55f43cdd1 100644 --- a/lib/services/coins/wownero/wownero_wallet.dart +++ b/lib/services/coins/wownero/wownero_wallet.dart @@ -714,12 +714,8 @@ class WowneroWallet extends CoinServiceAPI { final wallet = await _walletCreationService?.create(credentials); // subtract a couple days to ensure we have a buffer for SWB - if (seedWordsLength == 14) { - final bufferedCreateHeight = getSeedHeightSync(wallet?.seed.trim() as String); - } else { - final bufferedCreateHeight = 0; - // TODO use an alternative to wow_seed's get_seed_height - } + final bufferedCreateHeight = (seedWordsLength == 14) ? getSeedHeightSync(wallet?.seed.trim() as String) : 0; + // TODO use an alternative to wow_seed's get_seed_height instead of 0 above await DB.instance.put( boxName: walletId, key: "restoreHeight", value: bufferedCreateHeight); From 015f33326948aff663299206ed47ba3157c31a42 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 8 Nov 2022 14:33:16 -0600 Subject: [PATCH 13/17] do not rely upon nullable variable --- crypto_plugins/flutter_libmonero | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero index 2d5f5e563..0b355aee5 160000 --- a/crypto_plugins/flutter_libmonero +++ b/crypto_plugins/flutter_libmonero @@ -1 +1 @@ -Subproject commit 2d5f5e5636bdc4b211b2236492268167b5b969d0 +Subproject commit 0b355aee55608f497ca54aba151d0b3e9e2c4579 From c66e382fc380c45afb15690fbb69467ba1bde0d8 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 8 Nov 2022 17:30:09 -0600 Subject: [PATCH 14/17] get appropriate WowneroWordList based on seed length --- crypto_plugins/flutter_libmonero | 2 +- .../restore_wallet_view/restore_wallet_view.dart | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero index 0b355aee5..e440e9a3a 160000 --- a/crypto_plugins/flutter_libmonero +++ b/crypto_plugins/flutter_libmonero @@ -1 +1 @@ -Subproject commit 0b355aee55608f497ca54aba151d0b3e9e2c4579 +Subproject commit e440e9a3a125ee2030551ad7dea9114dd6a06aa0 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 a6b7e7e77..7596d7ac8 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'; @@ -149,13 +150,17 @@ class _RestoreWalletViewState extends ConsumerState { super.dispose(); } - // TODO: check for wownero wordlist? bool _isValidMnemonicWord(String word) { // TODO: get the actual language if (widget.coin == Coin.monero) { 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); } From a54d9a561e50593b6fe13a204776d631f7545f77 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 8 Nov 2022 23:10:27 -0600 Subject: [PATCH 15/17] track changes in flutter_libmonero --- crypto_plugins/flutter_libmonero | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero index e440e9a3a..9267fd0f0 160000 --- a/crypto_plugins/flutter_libmonero +++ b/crypto_plugins/flutter_libmonero @@ -1 +1 @@ -Subproject commit e440e9a3a125ee2030551ad7dea9114dd6a06aa0 +Subproject commit 9267fd0f0442a8d54b899473d77fc92ddc6d2391 From 357b93d6e897a2ece44eacdee8c32eac578177df Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 9 Nov 2022 00:16:21 -0600 Subject: [PATCH 16/17] use wownero.getHeightByDate and save bufferedHeight upon Monero wallet creation --- crypto_plugins/flutter_libmonero | 2 +- .../restore_wallet_view.dart | 6 ++--- lib/services/coins/monero/monero_wallet.dart | 2 +- .../coins/wownero/wownero_wallet.dart | 23 +++++++++++++------ 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero index 9267fd0f0..e705ba2d5 160000 --- a/crypto_plugins/flutter_libmonero +++ b/crypto_plugins/flutter_libmonero @@ -1 +1 @@ -Subproject commit 9267fd0f0442a8d54b899473d77fc92ddc6d2391 +Subproject commit e705ba2d5126685adae9367b62921b676d7126ed 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 7596d7ac8..913302a98 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 @@ -186,11 +186,9 @@ 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) { - // height = wownero.getHeightByDate(date: widget.restoreFromDate); - // } // TODO: make more robust estimate of date maybe using https://explorer.epic.tech/api-index if (widget.coin == Coin.epicCash) { diff --git a/lib/services/coins/monero/monero_wallet.dart b/lib/services/coins/monero/monero_wallet.dart index b0ebac4e6..8e7873014 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 55f43cdd1..0134cb1fe 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'; @@ -685,10 +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", - seedWordsLength: seedWordsLength - ); + name: name, language: "English", seedWordsLength: seedWordsLength); walletInfo = WalletInfo.external( id: WalletBase.idFor(name, WalletType.wownero), @@ -713,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 = (seedWordsLength == 14) ? getSeedHeightSync(wallet?.seed.trim() as String) : 0; - // TODO use an alternative to wow_seed's get_seed_height instead of 0 above + 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); @@ -979,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 From d15f022c4d6d86c5341972fb6c4824b24a01a503 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 9 Nov 2022 14:09:12 -0600 Subject: [PATCH 17/17] update wownero's first blocks per month --- crypto_plugins/flutter_libmonero | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero index e705ba2d5..b9bc2dcc5 160000 --- a/crypto_plugins/flutter_libmonero +++ b/crypto_plugins/flutter_libmonero @@ -1 +1 @@ -Subproject commit e705ba2d5126685adae9367b62921b676d7126ed +Subproject commit b9bc2dcc56e13f235a6c5b0fc02c0e543eb87758