CW-711 passphrase for XMR/WOWcreation (#1992)
* add monero passphrase add wownero passphrase add passphrase to seed screen * obscure passphrase by default disable passphrase create for zano * Update lib/view_model/wallet_keys_view_model.dart [skip ci] * Update lib/src/screens/wallet_keys/wallet_keys_page.dart [skip ci] * Update lib/view_model/advanced_privacy_settings_view_model.dart * dynamic passphrase icon * fix polyseed not being encrypted by passphrase --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
cyan committed
Feb 1, 2025 at 02:43 UTC
8b56c52dc6248eca62b7833471e67718ca8933da
15 files changed
+138
-20
cw_monero/lib/api/wallet.dart
+3
-4
@@ -45,12 +45,11 @@ String getSeed() {
45
if (cakepolyseed != "") {
46
if (cakepassphrase != "") {
47
try {
48
- final lang = PolyseedLang.getByPhrase(cakepassphrase);
48
+ final lang = PolyseedLang.getByPhrase(cakepolyseed);
49
final coin = PolyseedCoin.POLYSEED_MONERO;
50
final ps = Polyseed.decode(cakepolyseed, lang, coin);
51
- final passphrase = getPassphrase();
52
- if (ps.isEncrypted || passphrase == "") return ps.encode(lang, coin);
53
- ps.crypt(getPassphrase());
51
+ if (ps.isEncrypted || cakepassphrase == "") return ps.encode(lang, coin);
52
+ ps.crypt(cakepassphrase);
53
return ps.encode(lang, coin);
54
} catch (e) {
55
printV(e);
cw_monero/lib/api/wallet_manager.dart
+6
-1
@@ -70,6 +70,7 @@ void createWalletSync(
70
{required String path,
71
required String password,
72
required String language,
73
+ required String passphrase,
74
int nettype = 0}) {
75
txhistory = null;
76
final newWptr = monero.WalletManager_createWallet(wmPtr,
@@ -80,6 +81,7 @@ void createWalletSync(
81
throw WalletCreationException(message: monero.Wallet_errorString(newWptr));
82
}
83
wptr = newWptr;
84
+ monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: passphrase);
85
monero.Wallet_store(wptr!, path: path);
86
openedWalletsByPath[path] = wptr!;
87
_lastOpenedWallet = path;
@@ -390,8 +392,9 @@ void _createWallet(Map<String, dynamic> args) {
392
final path = args['path'] as String;
393
final password = args['password'] as String;
394
final language = args['language'] as String;
395
+ final passphrase = args['passphrase'] as String;
396
394
- createWalletSync(path: path, password: password, language: language);
397
+ createWalletSync(path: path, password: password, language: language, passphrase: passphrase);
398
}
399
400
void _restoreFromSeed(Map<String, dynamic> args) {
@@ -459,11 +462,13 @@ Future<void> createWallet(
462
{required String path,
463
required String password,
464
required String language,
465
+ required String passphrase,
466
int nettype = 0}) async =>
467
_createWallet({
468
'path': path,
469
'password': password,
470
'language': language,
471
+ 'passphrase': passphrase,
472
'nettype': nettype
473
});
474
cw_monero/lib/monero_wallet.dart
+1
-1
@@ -135,7 +135,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
135
passphrase: monero_wallet.getPassphrase());
136
137
int? get restoreHeight =>
138
- transactionHistory.transactions.values.firstOrNull?.height;
138
+ transactionHistory.transactions.values.firstOrNull?.height ?? monero.Wallet_getRefreshFromBlockHeight(wptr!);
139
140
monero_wallet.SyncListener? _listener;
141
ReactionDisposer? _onAccountChangeReaction;
cw_monero/lib/monero_wallet_service.dart
+8
-3
@@ -26,11 +26,12 @@ import 'package:polyseed/polyseed.dart';
26
27
class MoneroNewWalletCredentials extends WalletCredentials {
28
MoneroNewWalletCredentials(
29
- {required String name, required this.language, required this.isPolyseed, String? password})
29
+ {required String name, required this.language, required this.isPolyseed, String? password, this.passphrase})
30
: super(name: name, password: password);
31
32
final String language;
33
final bool isPolyseed;
34
+ final String? passphrase;
35
}
36
37
class MoneroRestoreWalletFromHardwareCredentials extends WalletCredentials {
@@ -92,7 +93,7 @@ class MoneroWalletService extends WalletService<
93
@override
94
WalletType getType() => WalletType.monero;
95
95
- @override
96
+ @override
97
Future<MoneroWallet> create(MoneroNewWalletCredentials credentials, {bool? isTestnet}) async {
98
try {
99
final path = await pathForWallet(name: credentials.name, type: getType());
@@ -112,7 +113,7 @@ class MoneroWalletService extends WalletService<
113
}
114
115
await monero_wallet_manager.createWallet(
115
- path: path, password: credentials.password!, language: credentials.language);
116
+ path: path, password: credentials.password!, language: credentials.language, passphrase: credentials.passphrase??"");
117
final wallet = MoneroWallet(
118
walletInfo: credentials.walletInfo!,
119
unspentCoinsInfo: unspentCoinsInfoSource,
@@ -382,6 +383,10 @@ class MoneroWalletService extends WalletService<
383
restoreHeight: height,
384
spendKey: spendKey);
385
386
+
387
+ monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed);
388
+ monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: passphrase??'');
389
+
390
final wallet = MoneroWallet(
391
walletInfo: walletInfo,
392
unspentCoinsInfo: unspentCoinsInfoSource,
cw_wownero/lib/api/wallet.dart
+1
-1
@@ -44,7 +44,7 @@ String getSeed() {
44
if (cakepolyseed != "") {
45
if (cakepassphrase != "") {
46
try {
47
- final lang = PolyseedLang.getByPhrase(cakepassphrase);
47
+ final lang = PolyseedLang.getByPhrase(cakepolyseed);
48
final coin = PolyseedCoin.POLYSEED_WOWNERO;
49
final ps = Polyseed.decode(cakepolyseed, lang, coin);
50
final passphrase = getPassphrase();
cw_wownero/lib/api/wallet_manager.dart
+7
-1
@@ -66,6 +66,7 @@ void createWalletSync(
66
{required String path,
67
required String password,
68
required String language,
69
+ required String passphrase,
70
int nettype = 0}) {
71
txhistory = null;
72
final newWptr = wownero.WalletManager_createWallet(wmPtr,
@@ -76,6 +77,8 @@ void createWalletSync(
77
throw WalletCreationException(message: wownero.Wallet_errorString(newWptr));
78
}
79
wptr = newWptr;
80
+ wownero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: passphrase);
81
+
82
wownero.Wallet_store(wptr!, path: path);
83
openedWalletsByPath[path] = wptr!;
84
@@ -362,8 +365,9 @@ void _createWallet(Map<String, dynamic> args) {
365
final path = args['path'] as String;
366
final password = args['password'] as String;
367
final language = args['language'] as String;
368
+ final passphrase = args['passphrase'] as String;
369
366
- createWalletSync(path: path, password: password, language: language);
370
+ createWalletSync(path: path, password: password, language: language, passphrase: passphrase);
371
}
372
373
void _restoreFromSeed(Map<String, dynamic> args) {
@@ -431,11 +435,13 @@ Future<void> createWallet(
435
{required String path,
436
required String password,
437
required String language,
438
+ required String passphrase,
439
int nettype = 0}) async =>
440
_createWallet({
441
'path': path,
442
'password': password,
443
'language': language,
444
+ 'passphrase': passphrase,
445
'nettype': nettype
446
});
447
cw_wownero/lib/wownero_wallet.dart
+5
@@ -20,6 +20,7 @@ import 'package:cw_core/wallet_base.dart';
20
import 'package:cw_core/wallet_info.dart';
21
import 'package:cw_core/wownero_amount_format.dart';
22
import 'package:cw_core/wownero_balance.dart';
23
+import 'package:cw_wownero/api/account_list.dart';
24
import 'package:cw_wownero/api/coins_info.dart';
25
import 'package:cw_wownero/api/structs/pending_transaction.dart';
26
import 'package:cw_wownero/api/transaction_history.dart' as transaction_history;
@@ -131,6 +132,10 @@ abstract class WowneroWalletBase
132
publicViewKey: wownero_wallet.getPublicViewKey(),
133
passphrase: wownero_wallet.getPassphrase());
134
135
+ int? get restoreHeight =>
136
+ transactionHistory.transactions.values.firstOrNull?.height ?? wownero.Wallet_getRefreshFromBlockHeight(wptr!);
137
+
138
+
139
wownero_wallet.SyncListener? _listener;
140
ReactionDisposer? _onAccountChangeReaction;
141
ReactionDisposer? _onTxHistoryChangeReaction;
cw_wownero/lib/wownero_wallet_service.dart
+7
-2
@@ -10,6 +10,7 @@ import 'package:cw_core/wallet_info.dart';
10
import 'package:cw_core/wallet_service.dart';
11
import 'package:cw_core/wallet_type.dart';
12
import 'package:cw_core/get_height_by_date.dart';
13
+import 'package:cw_wownero/api/account_list.dart';
14
import 'package:cw_wownero/api/exceptions/wallet_opening_exception.dart';
15
import 'package:cw_wownero/api/wallet_manager.dart' as wownero_wallet_manager;
16
import 'package:cw_wownero/api/wallet_manager.dart';
@@ -21,11 +22,12 @@ import 'package:monero/wownero.dart' as wownero;
22
23
class WowneroNewWalletCredentials extends WalletCredentials {
24
WowneroNewWalletCredentials(
24
- {required String name, required this.language, required this.isPolyseed, String? password})
25
+ {required String name, required this.language, required this.isPolyseed, this.passphrase, String? password})
26
: super(name: name, password: password);
27
28
final String language;
29
final bool isPolyseed;
30
+ final String? passphrase;
31
}
32
33
class WowneroRestoreWalletFromSeedCredentials extends WalletCredentials {
@@ -95,7 +97,7 @@ class WowneroWalletService extends WalletService<
97
}
98
99
await wownero_wallet_manager.createWallet(
98
- path: path, password: credentials.password!, language: credentials.language);
100
+ path: path, password: credentials.password!, language: credentials.language, passphrase: credentials.passphrase??'');
101
final wallet = WowneroWallet(
102
walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource, password: credentials.password!);
103
await wallet.init();
@@ -345,6 +347,9 @@ class WowneroWalletService extends WalletService<
347
restoreHeight: height,
348
spendKey: spendKey);
349
350
+ wownero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed);
351
+ wownero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: passphrase??'');
352
+
353
final wallet = WowneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource, password: password);
354
await wallet.init();
355
lib/monero/cw_monero.dart
+2
-1
@@ -253,9 +253,10 @@ class CWMonero extends Monero {
253
required String name,
254
required String language,
255
required bool isPolyseed,
256
+ required String? passphrase,
257
String? password}) =>
258
MoneroNewWalletCredentials(
258
- name: name, password: password, language: language, isPolyseed: isPolyseed);
259
+ name: name, password: password, language: language, isPolyseed: isPolyseed, passphrase: passphrase);
260
261
@override
262
Map<String, String> getKeys(Object wallet) {
lib/src/screens/wallet_keys/wallet_keys_page.dart
+64
@@ -1,3 +1,5 @@
1
+import 'dart:math';
2
+
3
import 'package:cake_wallet/entities/qr_view_data.dart';
4
import 'package:cake_wallet/generated/i18n.dart';
5
import 'package:cake_wallet/routes.dart';
@@ -15,6 +17,8 @@ import 'package:cake_wallet/utils/show_bar.dart';
17
import 'package:cake_wallet/view_model/wallet_keys_view_model.dart';
18
import 'package:flutter/material.dart';
19
import 'package:flutter/services.dart';
20
+import 'package:flutter_mobx/flutter_mobx.dart';
21
+import 'package:mobx/mobx.dart';
22
import 'package:qr_flutter/qr_flutter.dart';
23
24
class WalletKeysPage extends BasePage {
@@ -162,6 +166,8 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
166
children: [
167
if (isLegacySeedOnly || isLegacySeed) _buildHeightBox(),
168
const SizedBox(height: 20),
169
+ (_buildPassphraseBox() ?? Container()),
170
+ if (widget.walletKeysViewModel.passphrase.isNotEmpty) const SizedBox(height: 20),
171
Expanded(
172
child: SeedPhraseGridWidget(
173
list: isLegacySeed
@@ -262,6 +268,64 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
268
),
269
);
270
}
271
+
272
+
273
+ Widget? _buildPassphraseBox() {
274
+ if (widget.walletKeysViewModel.passphrase.isEmpty) return null;
275
+ return Container(
276
+ padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 14),
277
+ decoration: BoxDecoration(
278
+ borderRadius: BorderRadius.circular(8),
279
+ color: Theme.of(context).cardColor,
280
+ ),
281
+ child: Row(
282
+ mainAxisAlignment: MainAxisAlignment.center,
283
+ children: [
284
+ Text(
285
+ S.of(context).passphrase_view_keys,
286
+ textAlign: TextAlign.center,
287
+ style: TextStyle(
288
+ fontSize: 14,
289
+ fontWeight: FontWeight.w500,
290
+ color: Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor.withOpacity(0.5),
291
+ ),
292
+ ),
293
+ const SizedBox(width: 6),
294
+ Expanded(
295
+ child: Row(
296
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
297
+ children: [
298
+ Observer(builder: (BuildContext context) {
299
+ return Text(
300
+ (widget.walletKeysViewModel.obscurePassphrase) ?
301
+ "*****" :
302
+ widget.walletKeysViewModel.passphrase,
303
+ style: TextStyle(
304
+ fontSize: 14,
305
+ fontWeight: FontWeight.w500,
306
+ color: Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor,
307
+ ),
308
+ );
309
+ }),
310
+ Observer(builder: (BuildContext context) {
311
+ return GestureDetector(
312
+ onTap: () {
313
+ widget.walletKeysViewModel.obscurePassphrase = !widget.walletKeysViewModel.obscurePassphrase;
314
+ },
315
+ child: Icon(
316
+ widget.walletKeysViewModel.obscurePassphrase ? Icons.visibility_off : Icons.visibility,
317
+ size: 16,
318
+ color: Theme.of(context).textTheme.bodyLarge?.color?.withOpacity(0.7),
319
+ )
320
+ );
321
+ }),
322
+ ],
323
+ ),
324
+ ),
325
+ ],
326
+ ),
327
+ );
328
+ }
329
330
331
Widget _buildBottomActionPanel({
lib/view_model/advanced_privacy_settings_view_model.dart
+2
-2
@@ -78,8 +78,8 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
78
WalletType.ethereum,
79
WalletType.polygon,
80
WalletType.tron,
81
- if (isRestore) WalletType.monero,
82
- if (isRestore) WalletType.wownero,
81
+ WalletType.monero,
82
+ WalletType.wownero,
83
if (isRestore) WalletType.zano,
84
].contains(type);
85
lib/view_model/wallet_keys_view_model.dart
+18
@@ -86,9 +86,21 @@ abstract class WalletKeysViewModelBase with Store {
86
if (_wallet.type == WalletType.monero) {
87
return monero!.getRestoreHeight(_wallet)?.toString() ?? '';
88
}
89
+ if (_wallet.type == WalletType.wownero) {
90
+ return wownero!.getRestoreHeight(_wallet)?.toString() ?? '';
91
+ }
92
return '';
93
}
94
95
+
96
+
97
+ @observable
98
+ bool obscurePassphrase = true;
99
+
100
+ String get passphrase {
101
+ return _wallet.passphrase ?? '';
102
+ }
103
+
104
/// The Regex split the words based on any whitespace character.
105
///
106
/// Either standard ASCII space (U+0020) or the full-width space character (U+3000) used by the Japanese.
@@ -309,6 +321,12 @@ abstract class WalletKeysViewModelBase with Store {
321
}
322
323
Future<String?> get restoreHeight async {
324
+ if (_wallet.type == WalletType.monero) {
325
+ return monero!.getRestoreHeight(_wallet)?.toString();
326
+ }
327
+ if (_wallet.type == WalletType.wownero) {
328
+ return wownero!.getRestoreHeight(_wallet)?.toString();
329
+ }
330
if (_restoreHeightByTransactions != 0)
331
return getRoundedRestoreHeight(_restoreHeightByTransactions);
332
if (_restoreHeight != 0) return _restoreHeight.toString();
lib/view_model/wallet_new_vm.dart
+2
@@ -100,6 +100,7 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
100
name: name,
101
language: options!.first as String,
102
password: walletPassword,
103
+ passphrase: passphrase,
104
isPolyseed: options.last as bool);
105
case WalletType.bitcoin:
106
case WalletType.litecoin:
@@ -168,6 +169,7 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
169
language: options!.first as String,
170
isPolyseed: options.last as bool,
171
password: walletPassword,
172
+ passphrase: passphrase,
173
);
174
case WalletType.zano:
175
return zano!.createZanoNewWalletCredentials(
lib/wownero/cw_wownero.dart
+9
-2
@@ -233,9 +233,10 @@ class CWWownero extends Wownero {
233
{required String name,
234
required String language,
235
required bool isPolyseed,
236
- String? password}) =>
236
+ String? password,
237
+ String? passphrase}) =>
238
WowneroNewWalletCredentials(
238
- name: name, password: password, language: language, isPolyseed: isPolyseed);
239
+ name: name, password: password, language: language, isPolyseed: isPolyseed, passphrase: passphrase);
240
241
@override
242
Map<String, String> getKeys(Object wallet) {
@@ -250,6 +251,12 @@ class CWWownero extends Wownero {
251
};
252
}
253
254
+ @override
255
+ int? getRestoreHeight(Object wallet) {
256
+ final wowneroWallet = wallet as WowneroWallet;
257
+ return wowneroWallet.restoreHeight;
258
+ }
259
+
260
@override
261
Object createWowneroTransactionCreationCredentials(
262
{required List<Output> outputs, required TransactionPriority priority}) =>
tool/configure.dart
+3
-2
@@ -406,7 +406,7 @@ abstract class Monero {
406
required int height});
407
WalletCredentials createMoneroRestoreWalletFromSeedCredentials({required String name, required String password, required String passphrase, required int height, required String mnemonic});
408
WalletCredentials createMoneroRestoreWalletFromHardwareCredentials({required String name, required String password, required int height, required ledger.LedgerConnection ledgerConnection});
409
- WalletCredentials createMoneroNewWalletCredentials({required String name, required String language, required bool isPolyseed, String? password});
409
+ WalletCredentials createMoneroNewWalletCredentials({required String name, required String language, required bool isPolyseed, required String? passphrase, String? password});
410
Map<String, String> getKeys(Object wallet);
411
int? getRestoreHeight(Object wallet);
412
Object createMoneroTransactionCreationCredentials({required List<Output> outputs, required TransactionPriority priority});
@@ -595,7 +595,8 @@ abstract class Wownero {
595
required String language,
596
required int height});
597
WalletCredentials createWowneroRestoreWalletFromSeedCredentials({required String name, required String password, required String passphrase, required int height, required String mnemonic});
598
- WalletCredentials createWowneroNewWalletCredentials({required String name, required String language, required bool isPolyseed, String? password});
598
+ WalletCredentials createWowneroNewWalletCredentials({required String name, required String language, required bool isPolyseed, String? password, String? passphrase});
599
+ int? getRestoreHeight(Object wallet);
600
Map<String, String> getKeys(Object wallet);
601
Object createWowneroTransactionCreationCredentials({required List<Output> outputs, required TransactionPriority priority});
602
Object createWowneroTransactionCreationCredentialsRaw({required List<OutputInfo> outputs, required TransactionPriority priority});