Nano rep page + derivation fixes (#1655)
* minor nano derivation fixes * rep page fixes * ignore non-critical receive block errors * really be sure the derivation type is set during wallet creation
Matthew Fosse committed
Sep 4, 2024 at 19:41 UTC
783f1a234936dd75989956596159dd8a16a53b6f
4 files changed
+80
-59
cw_nano/lib/nano_client.dart
+18
-14
@@ -466,21 +466,25 @@ class NanoClient {
466
467
blocks = blocks as Map<String, dynamic>;
468
469
- // confirm all receivable blocks:
470
- for (final blockHash in blocks.keys) {
471
- final block = blocks[blockHash];
472
- final String amountRaw = block["amount"] as String;
473
- await receiveBlock(
474
- blockHash: blockHash,
475
- amountRaw: amountRaw,
476
- privateKey: privateKey,
477
- destinationAddress: destinationAddress,
478
- );
479
- // a bit of a hack:
480
- await Future<void>.delayed(const Duration(seconds: 2));
469
+ try {
470
+ // confirm all receivable blocks:
471
+ for (final blockHash in blocks.keys) {
472
+ final block = blocks[blockHash];
473
+ final String amountRaw = block["amount"] as String;
474
+ await receiveBlock(
475
+ blockHash: blockHash,
476
+ amountRaw: amountRaw,
477
+ privateKey: privateKey,
478
+ destinationAddress: destinationAddress,
479
+ );
480
+ // a bit of a hack:
481
+ await Future<void>.delayed(const Duration(seconds: 2));
482
+ }
483
+ return blocks.keys.length;
484
+ } catch (_) {
485
+ // we failed to confirm all receivable blocks for w/e reason (PoW / node outage / etc)
486
+ return 0;
487
}
482
-
483
- return blocks.keys.length;
488
}
489
490
void stop() {}
cw_nano/lib/nano_wallet_service.dart
+24
-7
@@ -14,8 +14,11 @@ import 'package:bip39/bip39.dart' as bip39;
14
import 'package:nanodart/nanodart.dart';
15
import 'package:nanoutil/nanoutil.dart';
16
17
-class NanoWalletService extends WalletService<NanoNewWalletCredentials,
18
- NanoRestoreWalletFromSeedCredentials, NanoRestoreWalletFromKeysCredentials, NanoNewWalletCredentials> {
17
+class NanoWalletService extends WalletService<
18
+ NanoNewWalletCredentials,
19
+ NanoRestoreWalletFromSeedCredentials,
20
+ NanoRestoreWalletFromKeysCredentials,
21
+ NanoNewWalletCredentials> {
22
NanoWalletService(this.walletInfoSource, this.isDirect);
23
24
final Box<WalletInfo> walletInfoSource;
@@ -33,8 +36,12 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
36
String seedKey = NanoSeeds.generateSeed();
37
String mnemonic = NanoDerivations.standardSeedToMnemonic(seedKey);
38
36
- // ensure default if not present:
37
- credentials.walletInfo!.derivationInfo ??= DerivationInfo(derivationType: DerivationType.nano);
39
+ // should never happen but just in case:
40
+ if (credentials.walletInfo!.derivationInfo == null) {
41
+ credentials.walletInfo!.derivationInfo = DerivationInfo(derivationType: DerivationType.nano);
42
+ } else if (credentials.walletInfo!.derivationInfo!.derivationType == null) {
43
+ credentials.walletInfo!.derivationInfo!.derivationType = DerivationType.nano;
44
+ }
45
46
final wallet = NanoWallet(
47
walletInfo: credentials.walletInfo!,
@@ -86,7 +93,8 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
93
}
94
95
@override
89
- Future<NanoWallet> restoreFromKeys(NanoRestoreWalletFromKeysCredentials credentials, {bool? isTestnet}) async {
96
+ Future<NanoWallet> restoreFromKeys(NanoRestoreWalletFromKeysCredentials credentials,
97
+ {bool? isTestnet}) async {
98
if (credentials.seedKey.contains(' ')) {
99
throw Exception("Invalid key!");
100
} else {
@@ -106,6 +114,13 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
114
}
115
}
116
117
+ // should never happen but just in case:
118
+ if (credentials.walletInfo!.derivationInfo == null) {
119
+ credentials.walletInfo!.derivationInfo = DerivationInfo(derivationType: DerivationType.nano);
120
+ } else if (credentials.walletInfo!.derivationInfo!.derivationType == null) {
121
+ credentials.walletInfo!.derivationInfo!.derivationType = DerivationType.nano;
122
+ }
123
+
124
final wallet = await NanoWallet(
125
password: credentials.password!,
126
mnemonic: mnemonic ?? credentials.seedKey,
@@ -119,11 +134,13 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
134
135
@override
136
Future<NanoWallet> restoreFromHardwareWallet(NanoNewWalletCredentials credentials) {
122
- throw UnimplementedError("Restoring a Nano wallet from a hardware wallet is not yet supported!");
137
+ throw UnimplementedError(
138
+ "Restoring a Nano wallet from a hardware wallet is not yet supported!");
139
}
140
141
@override
126
- Future<NanoWallet> restoreFromSeed(NanoRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async {
142
+ Future<NanoWallet> restoreFromSeed(NanoRestoreWalletFromSeedCredentials credentials,
143
+ {bool? isTestnet}) async {
144
if (credentials.mnemonic.contains(' ')) {
145
if (!bip39.validateMnemonic(credentials.mnemonic)) {
146
throw nm.NanoMnemonicIsIncorrectException();
lib/src/screens/nano/nano_change_rep_page.dart
+31
-33
@@ -40,13 +40,11 @@ class NanoChangeRepPage extends BasePage {
40
(node) => node.account == currentRepAccount,
41
orElse: () => N2Node(
42
account: currentRepAccount,
43
- alias: currentRepAccount,
43
score: 0,
44
uptime: "???",
45
weight: 0,
46
),
47
);
49
-
48
return currentNode;
49
}
50
@@ -57,9 +55,7 @@ class NanoChangeRepPage extends BasePage {
55
child: FutureBuilder(
56
future: nano!.getN2Reps(_wallet),
57
builder: (context, snapshot) {
60
- if (snapshot.data == null) {
61
- return SizedBox();
62
- }
58
+ final reps = snapshot.data ?? [];
59
60
return Container(
61
padding: EdgeInsets.only(left: 24, right: 24),
@@ -101,29 +97,35 @@ class NanoChangeRepPage extends BasePage {
97
),
98
_buildSingleRepresentative(
99
context,
104
- getCurrentRepNode(snapshot.data as List<N2Node>),
100
+ getCurrentRepNode(reps),
101
isList: false,
102
+ divider: false,
103
),
107
- Divider(height: 20),
108
- Container(
109
- margin: EdgeInsets.only(top: 12),
110
- child: Text(
111
- S.current.nano_pick_new_rep,
112
- style: TextStyle(
113
- fontSize: 16,
114
- fontWeight: FontWeight.w700,
104
+ if (reps.isNotEmpty) ...[
105
+ Divider(height: 20),
106
+ Container(
107
+ margin: EdgeInsets.only(top: 12),
108
+ child: Text(
109
+ S.current.nano_pick_new_rep,
110
+ style: TextStyle(
111
+ fontSize: 16,
112
+ fontWeight: FontWeight.w700,
113
+ ),
114
),
115
),
117
- ),
116
+ Divider(height: 20),
117
+ ],
118
],
119
),
120
],
121
),
122
contentPadding: EdgeInsets.only(bottom: 24),
123
content: Container(
124
- child: Column(
125
- children: _getRepresentativeWidgets(context, snapshot.data as List<N2Node>),
126
- ),
124
+ child: reps.isNotEmpty
125
+ ? Column(
126
+ children: _getRepresentativeWidgets(context, reps),
127
+ )
128
+ : SizedBox(),
129
),
130
bottomSectionPadding: EdgeInsets.only(bottom: 24),
131
bottomSection: Observer(
@@ -207,19 +209,22 @@ class NanoChangeRepPage extends BasePage {
209
final List<Widget> ret = [];
210
for (final N2Node node in list) {
211
if (node.alias != null && node.alias!.trim().isNotEmpty) {
210
- ret.add(_buildSingleRepresentative(context, node));
212
+ bool divider = node != list.first;
213
+ ret.add(_buildSingleRepresentative(context, node, divider: divider, isList: true));
214
}
215
}
216
return ret;
217
}
218
216
- Widget _buildSingleRepresentative(BuildContext context, N2Node rep, {bool isList = true}) {
219
+ Widget _buildSingleRepresentative(
220
+ BuildContext context,
221
+ N2Node rep, {
222
+ bool isList = true,
223
+ bool divider = false,
224
+ }) {
225
return Column(
226
children: <Widget>[
219
- if (isList)
220
- Divider(
221
- height: 2,
222
- ),
227
+ if (divider) Divider(height: 2),
228
TextButton(
229
style: TextButton.styleFrom(
230
padding: EdgeInsets.zero,
@@ -244,11 +249,11 @@ class NanoChangeRepPage extends BasePage {
249
crossAxisAlignment: CrossAxisAlignment.start,
250
children: <Widget>[
251
Text(
247
- _sanitizeAlias(rep.alias),
252
+ rep.alias ?? rep.account!,
253
style: TextStyle(
254
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
255
fontWeight: FontWeight.w700,
251
- fontSize: 18,
256
+ fontSize: rep.alias == null ? 14 : 18,
257
),
258
),
259
Container(
@@ -337,11 +342,4 @@ class NanoChangeRepPage extends BasePage {
342
],
343
);
344
}
340
-
341
- String _sanitizeAlias(String? alias) {
342
- if (alias != null) {
343
- return alias.replaceAll(RegExp(r'[^a-zA-Z_.!?_;:-]'), '');
344
- }
345
- return '';
346
- }
345
}
lib/view_model/wallet_restore_view_model.dart
+7
-5
@@ -42,7 +42,8 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
42
type == WalletType.tron,
43
isButtonEnabled = false,
44
mode = WalletRestoreMode.seed,
45
- super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel, type: type, isRecovery: true) {
45
+ super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel,
46
+ type: type, isRecovery: true) {
47
switch (type) {
48
case WalletType.monero:
49
availableModes = WalletRestoreMode.values;
@@ -194,10 +195,11 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
195
196
case WalletType.nano:
197
return nano!.createNanoRestoreWalletFromKeysCredentials(
197
- name: name,
198
- password: password,
199
- seedKey: options['private_key'] as String,
200
- derivationType: options["derivationType"] as DerivationType);
198
+ name: name,
199
+ password: password,
200
+ seedKey: options['private_key'] as String,
201
+ derivationType: derivationInfo!.derivationType!,
202
+ );
203
case WalletType.polygon:
204
return polygon!.createPolygonRestoreWalletFromPrivateKey(
205
name: name,