Nano rep page + derivation fixes (#1655)
Some checks are pending
Cache Dependencies / test (push) Waiting to run

* minor nano derivation fixes

* rep page fixes

* ignore non-critical receive block errors

* really be sure the derivation type is set during wallet creation
This commit is contained in:
Matthew Fosse 2024-09-04 19:41:52 -07:00 committed by GitHub
parent 576dd49766
commit 783f1a2349
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 80 additions and 59 deletions

View file

@ -466,21 +466,25 @@ class NanoClient {
blocks = blocks as Map<String, dynamic>; blocks = blocks as Map<String, dynamic>;
// confirm all receivable blocks: try {
for (final blockHash in blocks.keys) { // confirm all receivable blocks:
final block = blocks[blockHash]; for (final blockHash in blocks.keys) {
final String amountRaw = block["amount"] as String; final block = blocks[blockHash];
await receiveBlock( final String amountRaw = block["amount"] as String;
blockHash: blockHash, await receiveBlock(
amountRaw: amountRaw, blockHash: blockHash,
privateKey: privateKey, amountRaw: amountRaw,
destinationAddress: destinationAddress, privateKey: privateKey,
); destinationAddress: destinationAddress,
// a bit of a hack: );
await Future<void>.delayed(const Duration(seconds: 2)); // a bit of a hack:
await Future<void>.delayed(const Duration(seconds: 2));
}
return blocks.keys.length;
} catch (_) {
// we failed to confirm all receivable blocks for w/e reason (PoW / node outage / etc)
return 0;
} }
return blocks.keys.length;
} }
void stop() {} void stop() {}

View file

@ -14,8 +14,11 @@ import 'package:bip39/bip39.dart' as bip39;
import 'package:nanodart/nanodart.dart'; import 'package:nanodart/nanodart.dart';
import 'package:nanoutil/nanoutil.dart'; import 'package:nanoutil/nanoutil.dart';
class NanoWalletService extends WalletService<NanoNewWalletCredentials, class NanoWalletService extends WalletService<
NanoRestoreWalletFromSeedCredentials, NanoRestoreWalletFromKeysCredentials, NanoNewWalletCredentials> { NanoNewWalletCredentials,
NanoRestoreWalletFromSeedCredentials,
NanoRestoreWalletFromKeysCredentials,
NanoNewWalletCredentials> {
NanoWalletService(this.walletInfoSource, this.isDirect); NanoWalletService(this.walletInfoSource, this.isDirect);
final Box<WalletInfo> walletInfoSource; final Box<WalletInfo> walletInfoSource;
@ -33,8 +36,12 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
String seedKey = NanoSeeds.generateSeed(); String seedKey = NanoSeeds.generateSeed();
String mnemonic = NanoDerivations.standardSeedToMnemonic(seedKey); String mnemonic = NanoDerivations.standardSeedToMnemonic(seedKey);
// ensure default if not present: // should never happen but just in case:
credentials.walletInfo!.derivationInfo ??= DerivationInfo(derivationType: DerivationType.nano); if (credentials.walletInfo!.derivationInfo == null) {
credentials.walletInfo!.derivationInfo = DerivationInfo(derivationType: DerivationType.nano);
} else if (credentials.walletInfo!.derivationInfo!.derivationType == null) {
credentials.walletInfo!.derivationInfo!.derivationType = DerivationType.nano;
}
final wallet = NanoWallet( final wallet = NanoWallet(
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
@ -86,7 +93,8 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
} }
@override @override
Future<NanoWallet> restoreFromKeys(NanoRestoreWalletFromKeysCredentials credentials, {bool? isTestnet}) async { Future<NanoWallet> restoreFromKeys(NanoRestoreWalletFromKeysCredentials credentials,
{bool? isTestnet}) async {
if (credentials.seedKey.contains(' ')) { if (credentials.seedKey.contains(' ')) {
throw Exception("Invalid key!"); throw Exception("Invalid key!");
} else { } else {
@ -106,6 +114,13 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
} }
} }
// should never happen but just in case:
if (credentials.walletInfo!.derivationInfo == null) {
credentials.walletInfo!.derivationInfo = DerivationInfo(derivationType: DerivationType.nano);
} else if (credentials.walletInfo!.derivationInfo!.derivationType == null) {
credentials.walletInfo!.derivationInfo!.derivationType = DerivationType.nano;
}
final wallet = await NanoWallet( final wallet = await NanoWallet(
password: credentials.password!, password: credentials.password!,
mnemonic: mnemonic ?? credentials.seedKey, mnemonic: mnemonic ?? credentials.seedKey,
@ -119,11 +134,13 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
@override @override
Future<NanoWallet> restoreFromHardwareWallet(NanoNewWalletCredentials credentials) { Future<NanoWallet> restoreFromHardwareWallet(NanoNewWalletCredentials credentials) {
throw UnimplementedError("Restoring a Nano wallet from a hardware wallet is not yet supported!"); throw UnimplementedError(
"Restoring a Nano wallet from a hardware wallet is not yet supported!");
} }
@override @override
Future<NanoWallet> restoreFromSeed(NanoRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async { Future<NanoWallet> restoreFromSeed(NanoRestoreWalletFromSeedCredentials credentials,
{bool? isTestnet}) async {
if (credentials.mnemonic.contains(' ')) { if (credentials.mnemonic.contains(' ')) {
if (!bip39.validateMnemonic(credentials.mnemonic)) { if (!bip39.validateMnemonic(credentials.mnemonic)) {
throw nm.NanoMnemonicIsIncorrectException(); throw nm.NanoMnemonicIsIncorrectException();

View file

@ -40,13 +40,11 @@ class NanoChangeRepPage extends BasePage {
(node) => node.account == currentRepAccount, (node) => node.account == currentRepAccount,
orElse: () => N2Node( orElse: () => N2Node(
account: currentRepAccount, account: currentRepAccount,
alias: currentRepAccount,
score: 0, score: 0,
uptime: "???", uptime: "???",
weight: 0, weight: 0,
), ),
); );
return currentNode; return currentNode;
} }
@ -57,9 +55,7 @@ class NanoChangeRepPage extends BasePage {
child: FutureBuilder( child: FutureBuilder(
future: nano!.getN2Reps(_wallet), future: nano!.getN2Reps(_wallet),
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.data == null) { final reps = snapshot.data ?? [];
return SizedBox();
}
return Container( return Container(
padding: EdgeInsets.only(left: 24, right: 24), padding: EdgeInsets.only(left: 24, right: 24),
@ -101,29 +97,35 @@ class NanoChangeRepPage extends BasePage {
), ),
_buildSingleRepresentative( _buildSingleRepresentative(
context, context,
getCurrentRepNode(snapshot.data as List<N2Node>), getCurrentRepNode(reps),
isList: false, isList: false,
divider: false,
), ),
Divider(height: 20), if (reps.isNotEmpty) ...[
Container( Divider(height: 20),
margin: EdgeInsets.only(top: 12), Container(
child: Text( margin: EdgeInsets.only(top: 12),
S.current.nano_pick_new_rep, child: Text(
style: TextStyle( S.current.nano_pick_new_rep,
fontSize: 16, style: TextStyle(
fontWeight: FontWeight.w700, fontSize: 16,
fontWeight: FontWeight.w700,
),
), ),
), ),
), Divider(height: 20),
],
], ],
), ),
], ],
), ),
contentPadding: EdgeInsets.only(bottom: 24), contentPadding: EdgeInsets.only(bottom: 24),
content: Container( content: Container(
child: Column( child: reps.isNotEmpty
children: _getRepresentativeWidgets(context, snapshot.data as List<N2Node>), ? Column(
), children: _getRepresentativeWidgets(context, reps),
)
: SizedBox(),
), ),
bottomSectionPadding: EdgeInsets.only(bottom: 24), bottomSectionPadding: EdgeInsets.only(bottom: 24),
bottomSection: Observer( bottomSection: Observer(
@ -207,19 +209,22 @@ class NanoChangeRepPage extends BasePage {
final List<Widget> ret = []; final List<Widget> ret = [];
for (final N2Node node in list) { for (final N2Node node in list) {
if (node.alias != null && node.alias!.trim().isNotEmpty) { if (node.alias != null && node.alias!.trim().isNotEmpty) {
ret.add(_buildSingleRepresentative(context, node)); bool divider = node != list.first;
ret.add(_buildSingleRepresentative(context, node, divider: divider, isList: true));
} }
} }
return ret; return ret;
} }
Widget _buildSingleRepresentative(BuildContext context, N2Node rep, {bool isList = true}) { Widget _buildSingleRepresentative(
BuildContext context,
N2Node rep, {
bool isList = true,
bool divider = false,
}) {
return Column( return Column(
children: <Widget>[ children: <Widget>[
if (isList) if (divider) Divider(height: 2),
Divider(
height: 2,
),
TextButton( TextButton(
style: TextButton.styleFrom( style: TextButton.styleFrom(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
@ -244,11 +249,11 @@ class NanoChangeRepPage extends BasePage {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text( Text(
_sanitizeAlias(rep.alias), rep.alias ?? rep.account!,
style: TextStyle( style: TextStyle(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor, color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
fontSize: 18, fontSize: rep.alias == null ? 14 : 18,
), ),
), ),
Container( Container(
@ -337,11 +342,4 @@ class NanoChangeRepPage extends BasePage {
], ],
); );
} }
String _sanitizeAlias(String? alias) {
if (alias != null) {
return alias.replaceAll(RegExp(r'[^a-zA-Z_.!?_;:-]'), '');
}
return '';
}
} }

View file

@ -42,7 +42,8 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
type == WalletType.tron, type == WalletType.tron,
isButtonEnabled = false, isButtonEnabled = false,
mode = WalletRestoreMode.seed, mode = WalletRestoreMode.seed,
super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel, type: type, isRecovery: true) { super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel,
type: type, isRecovery: true) {
switch (type) { switch (type) {
case WalletType.monero: case WalletType.monero:
availableModes = WalletRestoreMode.values; availableModes = WalletRestoreMode.values;
@ -194,10 +195,11 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
case WalletType.nano: case WalletType.nano:
return nano!.createNanoRestoreWalletFromKeysCredentials( return nano!.createNanoRestoreWalletFromKeysCredentials(
name: name, name: name,
password: password, password: password,
seedKey: options['private_key'] as String, seedKey: options['private_key'] as String,
derivationType: options["derivationType"] as DerivationType); derivationType: derivationInfo!.derivationType!,
);
case WalletType.polygon: case WalletType.polygon:
return polygon!.createPolygonRestoreWalletFromPrivateKey( return polygon!.createPolygonRestoreWalletFromPrivateKey(
name: name, name: name,