dev
dart 498 lines 14.6 KB
Raw
1 import 'dart:ffi';
2 import 'dart:io';
3 import 'dart:isolate';
4
5 import 'package:cw_core/utils/print_verbose.dart';
6 import 'package:cw_wownero/api/account_list.dart';
7 import 'package:cw_wownero/api/exceptions/wallet_creation_exception.dart';
8 import 'package:cw_wownero/api/exceptions/wallet_opening_exception.dart';
9 import 'package:cw_wownero/api/exceptions/wallet_restore_from_keys_exception.dart';
10 import 'package:cw_wownero/api/exceptions/wallet_restore_from_seed_exception.dart';
11 import 'package:cw_wownero/api/transaction_history.dart';
12 import 'package:cw_wownero/api/wallet.dart';
13 import 'package:flutter/foundation.dart';
14 import 'package:monero/wownero.dart' as wownero;
15
16 class MoneroCException implements Exception {
17 final String message;
18
19 MoneroCException(this.message);
20
21 @override
22 String toString() {
23 return message;
24 }
25 }
26
27 void checkIfMoneroCIsFine() {
28 final cppCsCpp = wownero.WOWNERO_checksum_wallet2_api_c_cpp();
29 final cppCsH = wownero.WOWNERO_checksum_wallet2_api_c_h();
30 final cppCsExp = wownero.WOWNERO_checksum_wallet2_api_c_exp();
31
32 final dartCsCpp = wownero.wallet2_api_c_cpp_sha256;
33 final dartCsH = wownero.wallet2_api_c_h_sha256;
34 final dartCsExp = wownero.wallet2_api_c_exp_sha256;
35
36 if (cppCsCpp != dartCsCpp) {
37 throw MoneroCException(
38 "monero_c and monero.dart cpp wrapper code mismatch.\nLogic errors can occur.\nRefusing to run in release mode.\ncpp: '$cppCsCpp'\ndart: '$dartCsCpp'");
39 }
40
41 if (cppCsH != dartCsH) {
42 throw MoneroCException(
43 "monero_c and monero.dart cpp wrapper header mismatch.\nLogic errors can occur.\nRefusing to run in release mode.\ncpp: '$cppCsH'\ndart: '$dartCsH'");
44 }
45
46 if (cppCsExp != dartCsExp && (Platform.isIOS || Platform.isMacOS)) {
47 throw MoneroCException(
48 "monero_c and monero.dart wrapper export list mismatch.\nLogic errors can occur.\nRefusing to run in release mode.\ncpp: '$cppCsExp'\ndart: '$dartCsExp'");
49 }
50 }
51
52 wownero.WalletManager? _wmPtr;
53 final wownero.WalletManager wmPtr = Pointer.fromAddress((() {
54 try {
55 // Problems with the wallet? Crashes? Lags? this will print all calls to wow
56 // codebase, so it will be easier to debug what happens. At least easier
57 // than plugging gdb in. Especially on windows/android.
58 wownero.printStarts = false;
59 _wmPtr ??= wownero.WalletManagerFactory_getWalletManager();
60 printV("ptr: $_wmPtr");
61 } catch (e) {
62 printV(e);
63 rethrow;
64 }
65 return _wmPtr!.address;
66 })());
67
68 void createWalletSync(
69 {required String path,
70 required String password,
71 required String language,
72 required String passphrase,
73 int nettype = 0}) {
74 txhistory = null;
75 final newWptr = wownero.WalletManager_createWallet(wmPtr,
76 path: path, password: password, language: language, networkType: 0);
77
78 final status = wownero.Wallet_status(newWptr);
79 if (status != 0) {
80 throw WalletCreationException(message: wownero.Wallet_errorString(newWptr));
81 }
82 wptr = newWptr;
83 wownero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: passphrase);
84
85 wownero.Wallet_store(wptr!, path: path);
86 openedWalletsByPath[path] = wptr!;
87
88 // is the line below needed?
89 // setupNodeSync(address: "node.wowneroworld.com:18089");
90 }
91
92 bool isWalletExistSync({required String path}) {
93 return wownero.WalletManager_walletExists(wmPtr, path);
94 }
95
96 void restoreWalletFromSeedSync(
97 {required String path,
98 required String password,
99 required String passphrase,
100 required String seed,
101 int nettype = 0,
102 int restoreHeight = 0}) {
103 var newWptr;
104 if (seed.split(" ").length == 14) {
105 txhistory = null;
106 newWptr = wownero.WOWNERO_deprecated_restore14WordSeed(
107 path: path,
108 password: password,
109 language: seed, // I KNOW - this is supposed to be called seed
110 networkType: 0,
111 );
112 final oldwptr = wptr;
113 wptr = newWptr;
114 setRefreshFromBlockHeight(
115 height: wownero.WOWNERO_deprecated_14WordSeedHeight(seed: seed),
116 );
117 wptr = oldwptr;
118 } else {
119 txhistory = null;
120 newWptr = wownero.WalletManager_recoveryWallet(
121 wmPtr,
122 path: path,
123 password: password,
124 mnemonic: seed,
125 restoreHeight: restoreHeight,
126 seedOffset: passphrase,
127 networkType: 0,
128 );
129 }
130
131 final status = wownero.Wallet_status(newWptr);
132
133 if (status != 0) {
134 final error = wownero.Wallet_errorString(newWptr);
135 throw WalletRestoreFromSeedException(message: error);
136 }
137
138 wptr = newWptr;
139
140 wownero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: passphrase);
141 wownero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed);
142
143 openedWalletsByPath[path] = wptr!;
144
145 store();
146 }
147
148 void restoreWalletFromKeysSync(
149 {required String path,
150 required String password,
151 required String language,
152 required String address,
153 required String viewKey,
154 required String spendKey,
155 int nettype = 0,
156 int restoreHeight = 0}) {
157 txhistory = null;
158 var newWptr = (spendKey != "")
159 ? wownero.WalletManager_createDeterministicWalletFromSpendKey(wmPtr,
160 path: path,
161 password: password,
162 language: language,
163 spendKeyString: spendKey,
164 newWallet: true, // TODO(mrcyjanek): safe to remove
165 restoreHeight: restoreHeight)
166 : wownero.WalletManager_createWalletFromKeys(
167 wmPtr,
168 path: path,
169 password: password,
170 restoreHeight: restoreHeight,
171 addressString: address,
172 viewKeyString: viewKey,
173 spendKeyString: spendKey,
174 nettype: 0,
175 );
176
177 final status = wownero.Wallet_status(newWptr);
178 if (status != 0) {
179 throw WalletRestoreFromKeysException(message: wownero.Wallet_errorString(newWptr));
180 }
181 // CW-712 - Try to restore deterministic wallet first, if the view key doesn't
182 // match the view key provided
183 if (spendKey != "") {
184 final viewKeyRestored = wownero.Wallet_secretViewKey(newWptr);
185 if (viewKey != viewKeyRestored && viewKey != "") {
186 wownero.WalletManager_closeWallet(wmPtr, newWptr, false);
187 File(path).deleteSync();
188 File(path + ".keys").deleteSync();
189 newWptr = wownero.WalletManager_createWalletFromKeys(
190 wmPtr,
191 path: path,
192 password: password,
193 restoreHeight: restoreHeight,
194 addressString: address,
195 viewKeyString: viewKey,
196 spendKeyString: spendKey,
197 nettype: 0,
198 );
199 final status = wownero.Wallet_status(newWptr);
200 if (status != 0) {
201 throw WalletRestoreFromKeysException(message: wownero.Wallet_errorString(newWptr));
202 }
203 }
204 }
205 wptr = newWptr;
206
207 openedWalletsByPath[path] = wptr!;
208 }
209
210 // English only, because normalization.
211 void restoreWalletFromPolyseedWithOffset(
212 {required String path,
213 required String password,
214 required String seed,
215 required String seedOffset,
216 required String language,
217 int nettype = 0}) {
218 txhistory = null;
219 final newWptr = wownero.WalletManager_createWalletFromPolyseed(
220 wmPtr,
221 path: path,
222 password: password,
223 networkType: nettype,
224 mnemonic: seed,
225 seedOffset: seedOffset,
226 newWallet: true, // safe to remove
227 restoreHeight: 0,
228 kdfRounds: 1,
229 );
230
231 final status = wownero.Wallet_status(newWptr);
232
233 if (status != 0) {
234 final err = wownero.Wallet_errorString(newWptr);
235 printV("err: $err");
236 throw WalletRestoreFromKeysException(message: err);
237 }
238
239 wptr = newWptr;
240
241 wownero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed);
242 wownero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: seedOffset);
243
244 storeSync();
245
246 openedWalletsByPath[path] = wptr!;
247 }
248
249 void restoreWalletFromSpendKeySync(
250 {required String path,
251 required String password,
252 required String seed,
253 required String language,
254 required String spendKey,
255 int nettype = 0,
256 int restoreHeight = 0}) {
257 // txhistory = null;
258 // wptr = wownero.WalletManager_createWalletFromKeys(
259 // wmPtr,
260 // path: path,
261 // password: password,
262 // restoreHeight: restoreHeight,
263 // addressString: '',
264 // spendKeyString: spendKey,
265 // viewKeyString: '',
266 // nettype: 0,
267 // );
268
269 txhistory = null;
270 final newWptr = wownero.WalletManager_createDeterministicWalletFromSpendKey(
271 wmPtr,
272 path: path,
273 password: password,
274 language: language,
275 spendKeyString: spendKey,
276 newWallet: true, // TODO(mrcyjanek): safe to remove
277 restoreHeight: restoreHeight,
278 );
279
280 final status = wownero.Wallet_status(newWptr);
281
282 if (status != 0) {
283 final err = wownero.Wallet_errorString(newWptr);
284 printV("err: $err");
285 throw WalletRestoreFromKeysException(message: err);
286 }
287
288 wptr = newWptr;
289
290 wownero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed);
291
292 storeSync();
293
294 openedWalletsByPath[path] = wptr!;
295 }
296
297 String _lastOpenedWallet = "";
298
299 // void restoreWowneroWalletFromDevice(
300 // {required String path,
301 // required String password,
302 // required String deviceName,
303 // int nettype = 0,
304 // int restoreHeight = 0}) {
305 //
306 // final pathPointer = path.toNativeUtf8();
307 // final passwordPointer = password.toNativeUtf8();
308 // final deviceNamePointer = deviceName.toNativeUtf8();
309 // final errorMessagePointer = ''.toNativeUtf8();
310 //
311 // final isWalletRestored = restoreWalletFromDeviceNative(
312 // pathPointer,
313 // passwordPointer,
314 // deviceNamePointer,
315 // nettype,
316 // restoreHeight,
317 // errorMessagePointer) != 0;
318 //
319 // calloc.free(pathPointer);
320 // calloc.free(passwordPointer);
321 //
322 // storeSync();
323 //
324 // if (!isWalletRestored) {
325 // throw WalletRestoreFromKeysException(
326 // message: convertUTF8ToString(pointer: errorMessagePointer));
327 // }
328 // }
329
330 Map<String, wownero.wallet> openedWalletsByPath = {};
331
332 void loadWallet({required String path, required String password, int nettype = 0}) {
333 if (openedWalletsByPath[path] != null) {
334 txhistory = null;
335 wptr = openedWalletsByPath[path]!;
336 return;
337 }
338 if (wptr == null || path != _lastOpenedWallet) {
339 if (wptr != null) {
340 final addr = wptr!.address;
341 Isolate.run(() {
342 wownero.Wallet_store(Pointer.fromAddress(addr));
343 });
344 }
345 txhistory = null;
346 final newWptr = wownero.WalletManager_openWallet(wmPtr, path: path, password: password);
347
348 _lastOpenedWallet = path;
349 final status = wownero.Wallet_status(newWptr);
350 if (status != 0) {
351 final err = wownero.Wallet_errorString(newWptr);
352 printV(err);
353 throw WalletOpeningException(message: err);
354 }
355 wptr = newWptr;
356 openedWalletsByPath[path] = wptr!;
357 }
358 }
359
360 void _createWallet(Map<String, dynamic> args) {
361 final path = args['path'] as String;
362 final password = args['password'] as String;
363 final language = args['language'] as String;
364 final passphrase = args['passphrase'] as String;
365
366 createWalletSync(path: path, password: password, language: language, passphrase: passphrase);
367 }
368
369 void _restoreFromSeed(Map<String, dynamic> args) {
370 final path = args['path'] as String;
371 final password = args['password'] as String;
372 final passphrase = args['passphrase'] as String;
373 final seed = args['seed'] as String;
374 final restoreHeight = args['restoreHeight'] as int;
375
376 restoreWalletFromSeedSync(
377 path: path,
378 password: password,
379 passphrase: passphrase,
380 seed: seed,
381 restoreHeight: restoreHeight);
382 }
383
384 void _restoreFromKeys(Map<String, dynamic> args) {
385 final path = args['path'] as String;
386 final password = args['password'] as String;
387 final language = args['language'] as String;
388 final restoreHeight = args['restoreHeight'] as int;
389 final address = args['address'] as String;
390 final viewKey = args['viewKey'] as String;
391 final spendKey = args['spendKey'] as String;
392
393 restoreWalletFromKeysSync(
394 path: path,
395 password: password,
396 language: language,
397 restoreHeight: restoreHeight,
398 address: address,
399 viewKey: viewKey,
400 spendKey: spendKey);
401 }
402
403 void _restoreFromSpendKey(Map<String, dynamic> args) {
404 final path = args['path'] as String;
405 final password = args['password'] as String;
406 final seed = args['seed'] as String;
407 final language = args['language'] as String;
408 final spendKey = args['spendKey'] as String;
409 final restoreHeight = args['restoreHeight'] as int;
410
411 restoreWalletFromSpendKeySync(
412 path: path,
413 password: password,
414 seed: seed,
415 language: language,
416 restoreHeight: restoreHeight,
417 spendKey: spendKey);
418 }
419
420 Future<void> _openWallet(Map<String, String> args) async =>
421 loadWallet(path: args['path'] as String, password: args['password'] as String);
422
423 Future<bool> _isWalletExist(String path) async => isWalletExistSync(path: path);
424
425 void openWallet({required String path, required String password, int nettype = 0}) async =>
426 loadWallet(path: path, password: password, nettype: nettype);
427
428 Future<void> openWalletAsync(Map<String, String> args) async => _openWallet(args);
429
430 Future<void> createWallet(
431 {required String path,
432 required String password,
433 required String language,
434 required String passphrase,
435 int nettype = 0}) async =>
436 _createWallet({
437 'path': path,
438 'password': password,
439 'language': language,
440 'passphrase': passphrase,
441 'nettype': nettype
442 });
443
444 Future<void> restoreFromSeed(
445 {required String path,
446 required String password,
447 required String passphrase,
448 required String seed,
449 int nettype = 0,
450 int restoreHeight = 0}) async =>
451 _restoreFromSeed({
452 'path': path,
453 'password': password,
454 'passphrase': passphrase,
455 'seed': seed,
456 'nettype': nettype,
457 'restoreHeight': restoreHeight
458 });
459
460 Future<void> restoreFromKeys(
461 {required String path,
462 required String password,
463 required String language,
464 required String address,
465 required String viewKey,
466 required String spendKey,
467 int nettype = 0,
468 int restoreHeight = 0}) async =>
469 _restoreFromKeys({
470 'path': path,
471 'password': password,
472 'language': language,
473 'address': address,
474 'viewKey': viewKey,
475 'spendKey': spendKey,
476 'nettype': nettype,
477 'restoreHeight': restoreHeight
478 });
479
480 Future<void> restoreFromSpendKey(
481 {required String path,
482 required String password,
483 required String seed,
484 required String language,
485 required String spendKey,
486 int nettype = 0,
487 int restoreHeight = 0}) async =>
488 _restoreFromSpendKey({
489 'path': path,
490 'password': password,
491 'seed': seed,
492 'language': language,
493 'spendKey': spendKey,
494 'nettype': nettype,
495 'restoreHeight': restoreHeight
496 });
497
498 Future<bool> isWalletExist({required String path}) => _isWalletExist(path);