Skip to content

Commit 4c8a335

Browse files
Merge pull request #208 from cypherstack/wownero/25-word
Enable 25 word Wownero seed imports
2 parents 6afc912 + d15f022 commit 4c8a335

File tree

9 files changed

+665
-14
lines changed

9 files changed

+665
-14
lines changed

lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:bip39/src/wordlists/english.dart' as bip39wordlist;
88
import 'package:flutter/material.dart';
99
import 'package:flutter/services.dart';
1010
import 'package:flutter_libmonero/monero/monero.dart';
11+
import 'package:flutter_libmonero/wownero/wownero.dart';
1112
import 'package:flutter_riverpod/flutter_riverpod.dart';
1213
import 'package:flutter_svg/flutter_svg.dart';
1314
import 'package:stackwallet/notifications/show_flush_bar.dart';
@@ -156,6 +157,11 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
156157
var moneroWordList = monero.getMoneroWordList("English");
157158
return moneroWordList.contains(word);
158159
}
160+
if (widget.coin == Coin.wownero) {
161+
var wowneroWordList = wownero.getWowneroWordList("English",
162+
seedWordsLength: widget.seedWordsLength);
163+
return wowneroWordList.contains(word);
164+
}
159165
return _wordListHashSet.contains(word);
160166
}
161167

@@ -181,6 +187,8 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
181187

182188
if (widget.coin == Coin.monero) {
183189
height = monero.getHeigthByDate(date: widget.restoreFromDate);
190+
} else if (widget.coin == Coin.wownero) {
191+
height = wownero.getHeightByDate(date: widget.restoreFromDate);
184192
}
185193
// todo: wait until this implemented
186194
// else if (widget.coin == Coin.wownero) {

lib/services/coins/monero/monero_wallet.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,7 @@ class MoneroWallet extends CoinServiceAPI {
699699
name: name,
700700
type: WalletType.monero,
701701
isRecovery: false,
702-
restoreHeight: credentials.height ?? 0,
702+
restoreHeight: bufferedCreateHeight,
703703
date: DateTime.now(),
704704
path: path,
705705
dirPath: dirPath,

lib/services/coins/wownero/wownero_wallet.dart

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'dart:async';
22
import 'dart:io';
33

4+
import 'package:cw_core/get_height_by_date.dart';
45
import 'package:cw_core/monero_transaction_priority.dart';
56
import 'package:cw_core/node.dart';
67
import 'package:cw_core/pending_transaction.dart';
@@ -647,7 +648,7 @@ class WowneroWallet extends CoinServiceAPI {
647648
}
648649

649650
//TODO: take in the default language when creating wallet.
650-
Future<void> _generateNewWallet() async {
651+
Future<void> _generateNewWallet({int seedWordsLength = 14}) async {
651652
Logging.instance
652653
.log("IS_INTEGRATION_TEST: $integrationTestFlag", level: LogLevel.Info);
653654
// TODO: ping wownero server and make sure the genesis hash matches
@@ -685,9 +686,7 @@ class WowneroWallet extends CoinServiceAPI {
685686
await pathForWalletDir(name: name, type: WalletType.wownero);
686687
final path = await pathForWallet(name: name, type: WalletType.wownero);
687688
credentials = wownero.createWowneroNewWalletCredentials(
688-
name: name,
689-
language: "English",
690-
);
689+
name: name, language: "English", seedWordsLength: seedWordsLength);
691690

692691
walletInfo = WalletInfo.external(
693692
id: WalletBase.idFor(name, WalletType.wownero),
@@ -712,16 +711,20 @@ class WowneroWallet extends CoinServiceAPI {
712711
// To restore from a seed
713712
final wallet = await _walletCreationService?.create(credentials);
714713

715-
// subtract a couple days to ensure we have a buffer for SWB
716-
final bufferedCreateHeight =
717-
getSeedHeightSync(wallet?.seed.trim() as String);
714+
final bufferedCreateHeight = (seedWordsLength == 14)
715+
? getSeedHeightSync(wallet?.seed.trim() as String)
716+
: wownero.getHeightByDate(
717+
date: DateTime.now().subtract(const Duration(
718+
days:
719+
2))); // subtract a couple days to ensure we have a buffer for SWB
718720

719721
await DB.instance.put<dynamic>(
720722
boxName: walletId, key: "restoreHeight", value: bufferedCreateHeight);
721723
walletInfo.restoreHeight = bufferedCreateHeight;
722724

723725
await _secureStore.write(
724726
key: '${_walletId}_mnemonic', value: wallet?.seed.trim());
727+
725728
walletInfo.address = wallet?.walletAddresses.address;
726729
await DB.instance
727730
.add<WalletInfo>(boxName: WalletInfo.boxName, value: walletInfo);
@@ -778,7 +781,7 @@ class WowneroWallet extends CoinServiceAPI {
778781

779782
@override
780783
// TODO: implement initializeWallet
781-
Future<bool> initializeNew() async {
784+
Future<bool> initializeNew({int seedWordsLength = 14}) async {
782785
await _prefs.init();
783786
// TODO: ping actual wownero network
784787
// try {
@@ -796,7 +799,7 @@ class WowneroWallet extends CoinServiceAPI {
796799
prefs = await SharedPreferences.getInstance();
797800
keysStorage = KeyService(storage!);
798801

799-
await _generateNewWallet();
802+
await _generateNewWallet(seedWordsLength: seedWordsLength);
800803
// var password;
801804
// try {
802805
// password =
@@ -977,6 +980,14 @@ class WowneroWallet extends CoinServiceAPI {
977980
// extract seed height from 14 word seed
978981
if (seedLength == 14) {
979982
height = getSeedHeightSync(mnemonic.trim());
983+
} else {
984+
// 25 word seed. TODO validate
985+
if (height == 0) {
986+
height = wownero.getHeightByDate(
987+
date: DateTime.now().subtract(const Duration(
988+
days:
989+
2))); // subtract a couple days to ensure we have a buffer for SWB\
990+
}
980991
}
981992

982993
await DB.instance

lib/utilities/constants.dart

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,7 @@ abstract class Constants {
6262
values.addAll([25]);
6363
break;
6464
case Coin.wownero:
65-
values.addAll([14]);
66-
// todo: uncomment when wownero 25 word seeds implemented
67-
// values.addAll([14, 25]);
65+
values.addAll([14, 25]);
6866
break;
6967
}
7068
return values;
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
import 'dart:async';
2+
import 'dart:core';
3+
import 'dart:core' as core;
4+
import 'dart:io';
5+
import 'dart:math';
6+
7+
import 'package:flutter_test/flutter_test.dart';
8+
import 'package:hive/hive.dart';
9+
import 'package:hive_test/hive_test.dart';
10+
import 'package:mockito/annotations.dart';
11+
import 'package:mockito/mockito.dart';
12+
13+
import 'package:cw_core/monero_amount_format.dart';
14+
import 'package:cw_core/node.dart';
15+
import 'package:cw_core/pending_transaction.dart';
16+
import 'package:cw_core/unspent_coins_info.dart';
17+
import 'package:cw_core/wallet_base.dart';
18+
import 'package:cw_core/wallet_credentials.dart';
19+
import 'package:cw_core/wallet_info.dart';
20+
import 'package:cw_core/wallet_service.dart';
21+
import 'package:cw_core/wallet_type.dart';
22+
import 'package:cw_monero/api/wallet.dart';
23+
import 'package:cw_monero/api/wallet_manager.dart' as monero_wallet_manager;
24+
import 'package:cw_monero/pending_monero_transaction.dart';
25+
import 'package:cw_monero/monero_wallet.dart';
26+
import 'package:flutter/material.dart';
27+
import 'package:flutter/services.dart';
28+
import 'package:flutter_libmonero/core/key_service.dart';
29+
import 'package:flutter_libmonero/core/wallet_creation_service.dart';
30+
import 'package:flutter_libmonero/view_model/send/output.dart';
31+
import 'package:flutter_libmonero/monero/monero.dart';
32+
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
33+
import 'package:hive/hive.dart';
34+
import 'package:path_provider/path_provider.dart';
35+
import 'package:shared_preferences/shared_preferences.dart';
36+
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
37+
38+
import 'package:stackwallet/services/wallets.dart';
39+
40+
import 'dart:developer' as developer;
41+
42+
// TODO trim down to the minimum imports above
43+
44+
import 'monero_wallet_test_data.dart';
45+
46+
//FlutterSecureStorage? storage;
47+
FakeSecureStorage? storage;
48+
WalletService? walletService;
49+
SharedPreferences? prefs;
50+
KeyService? keysStorage;
51+
MoneroWalletBase? walletBase;
52+
late WalletCreationService _walletCreationService;
53+
dynamic _walletInfoSource;
54+
Wallets? walletsService;
55+
56+
String path = '';
57+
58+
String name = 'namee${Random().nextInt(10000000)}';
59+
int nettype = 0;
60+
WalletType type = WalletType.monero;
61+
62+
@GenerateMocks([])
63+
void main() async {
64+
storage = FakeSecureStorage();
65+
prefs = await SharedPreferences.getInstance();
66+
keysStorage = KeyService(storage!);
67+
WalletInfo walletInfo = WalletInfo.external(
68+
id: '',
69+
name: '',
70+
type: type,
71+
isRecovery: false,
72+
restoreHeight: 0,
73+
date: DateTime.now(),
74+
path: '',
75+
address: '',
76+
dirPath: '');
77+
late WalletCredentials credentials;
78+
79+
WidgetsFlutterBinding.ensureInitialized();
80+
Directory appDir = (await getApplicationDocumentsDirectory());
81+
if (Platform.isIOS) {
82+
appDir = (await getLibraryDirectory());
83+
}
84+
await Hive.close();
85+
Hive.init(appDir.path);
86+
Hive.registerAdapter(NodeAdapter());
87+
Hive.registerAdapter(WalletInfoAdapter());
88+
Hive.registerAdapter(WalletTypeAdapter());
89+
Hive.registerAdapter(UnspentCoinsInfoAdapter());
90+
91+
monero.onStartup();
92+
_walletInfoSource = await Hive.openBox<WalletInfo>(WalletInfo.boxName);
93+
walletService = monero.createMoneroWalletService(_walletInfoSource);
94+
95+
group("Mainnet tests", () {
96+
setUp(() async {
97+
try {
98+
// if (name?.isEmpty ?? true) {
99+
// name = await generateName();
100+
// }
101+
final dirPath = await pathForWalletDir(name: name, type: type);
102+
path = await pathForWallet(name: name, type: type);
103+
credentials =
104+
// // creating a new wallet
105+
// monero.createMoneroNewWalletCredentials(
106+
// name: name, language: "English");
107+
// restoring a previous wallet
108+
monero.createMoneroRestoreWalletFromSeedCredentials(
109+
name: name, height: 2580000, mnemonic: testMnemonic);
110+
111+
walletInfo = WalletInfo.external(
112+
id: WalletBase.idFor(name, type),
113+
name: name,
114+
type: type,
115+
isRecovery: false,
116+
restoreHeight: credentials.height ?? 0,
117+
date: DateTime.now(),
118+
path: path,
119+
address: "",
120+
dirPath: dirPath);
121+
credentials.walletInfo = walletInfo;
122+
123+
_walletCreationService = WalletCreationService(
124+
secureStorage: storage,
125+
sharedPreferences: prefs,
126+
walletService: walletService,
127+
keyService: keysStorage,
128+
);
129+
_walletCreationService.changeWalletType();
130+
} catch (e, s) {
131+
print(e);
132+
print(s);
133+
}
134+
});
135+
136+
test("Test mainnet address generation from seed", () async {
137+
final wallet = await
138+
// _walletCreationService.create(credentials);
139+
_walletCreationService.restoreFromSeed(credentials);
140+
walletInfo.address = wallet.walletAddresses.address;
141+
//print(walletInfo.address);
142+
143+
await _walletInfoSource.add(walletInfo);
144+
walletBase?.close();
145+
walletBase = wallet as MoneroWalletBase;
146+
//print("${walletBase?.seed}");
147+
148+
// print(walletBase);
149+
// loggerPrint(walletBase.toString());
150+
// loggerPrint("name: ${walletBase!.name} seed: ${walletBase!.seed} id: "
151+
// "${walletBase!.id} walletinfo: ${toStringForinfo(walletBase!.walletInfo)} type: ${walletBase!.type} balance: "
152+
// "${walletBase!.balance.entries.first.value.available} currency: ${walletBase!.currency}");
153+
154+
expect(walletInfo.address, mainnetTestData[0][0]);
155+
expect(
156+
await walletBase!.getTransactionAddress(0, 0), mainnetTestData[0][0]);
157+
expect(
158+
await walletBase!.getTransactionAddress(0, 1), mainnetTestData[0][1]);
159+
expect(
160+
await walletBase!.getTransactionAddress(0, 2), mainnetTestData[0][2]);
161+
expect(
162+
await walletBase!.getTransactionAddress(1, 0), mainnetTestData[1][0]);
163+
expect(
164+
await walletBase!.getTransactionAddress(1, 1), mainnetTestData[1][1]);
165+
expect(
166+
await walletBase!.getTransactionAddress(1, 2), mainnetTestData[1][2]);
167+
});
168+
});
169+
/*
170+
// 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
171+
group("Mainnet wallet deletion test", () {
172+
test("Test mainnet wallet existence", () {
173+
expect(monero_wallet_manager.isWalletExistSync(path: path), true);
174+
});
175+
176+
test("Test mainnet wallet deletion", () {
177+
// Remove wallet from wallet service
178+
walletService?.remove(name);
179+
walletsService?.removeWallet(walletId: name);
180+
expect(monero_wallet_manager.isWalletExistSync(path: path), false);
181+
});
182+
});
183+
184+
group("Mainnet node tests", () {
185+
test("Test mainnet node connection", () async {
186+
await walletBase?.connectToNode(
187+
node: Node(
188+
uri: "monero-stagenet.stackwallet.com:38081",
189+
type: WalletType.moneroStageNet));
190+
await walletBase!.rescan(
191+
height:
192+
credentials.height); // Probably shouldn't be rescanning from 0...
193+
await walletBase!.getNodeHeight();
194+
int height = await walletBase!.getNodeHeight();
195+
print('height: $height');
196+
bool connected = await walletBase!.isConnected();
197+
print('connected: $connected');
198+
199+
//expect...
200+
});
201+
});
202+
*/
203+
204+
// TODO test deletion of wallets ... and delete them
205+
}
206+
207+
Future<String> pathForWalletDir(
208+
{required String name, required WalletType type}) async {
209+
Directory root = (await getApplicationDocumentsDirectory());
210+
if (Platform.isIOS) {
211+
root = (await getLibraryDirectory());
212+
}
213+
final prefix = walletTypeToString(type).toLowerCase();
214+
final walletsDir = Directory('${root.path}/wallets');
215+
final walletDire = Directory('${walletsDir.path}/$prefix/$name');
216+
217+
if (!walletDire.existsSync()) {
218+
walletDire.createSync(recursive: true);
219+
}
220+
221+
return walletDire.path;
222+
}
223+
224+
Future<String> pathForWallet(
225+
{required String name, required WalletType type}) async =>
226+
await pathForWalletDir(name: name, type: type)
227+
.then((path) => path + '/$name');
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
String testMnemonic =
2+
'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';
3+
var mainnetTestData = [
4+
[
5+
'4AeRgkWZsMJhAWKMeCZ3h4ZSPnAcW5VBtRFyLd6gBEf6GgJU2FHXDA6i1DnQTd6h8R3VU5AkbGcWSNhtSwNNPgaD48gp4nn',
6+
'82WsoLmbZt3BPwJMF5PfT8GitThJzUq3FFoSQyr4fKfJdxZebgY3mHPcnAqTBA3FFwZRGxC4ZDwkfE1VVULPa55x3xXgCbj',
7+
'84kYPuZ1eaVKGQhf26QPNWbSLQG16BywXdLYYShVrPNMLAUAWce5vcpRc78FxwRphrG6Cda7faCKdUMr8fUCH3peHPenvHy'
8+
],
9+
[
10+
'86SF44CsTBYU3vk1X7nGBbQnrUSknGbd6Uw8a9hUUgy3KBeXTDvk3pm8upMzZKw17m3mLPEzbcPp5WLpYVoHR5PKNVtFrHH',
11+
'8Aa9LNGdBHwYUMsy6M9ZVXMEkTBZyEDT7aQmY32trCxbU6dwkZJSCSbcpyL7UiTB9QXXosomZtJYvUJ296vTNX5yQ81KaA2',
12+
'85C5zZRcaD89PKmXEwjcYMVAUqoH5rrAXe3GokvSupXnDmccYvZagz5Qem7bQLteEw4iFEJ9oRk9BNfjTi4K2cyTJbTMMPT'
13+
]
14+
];

0 commit comments

Comments
 (0)