This commit is contained in:
fosse 2023-08-14 18:02:12 -04:00
parent adc98f34cc
commit a4e7acf875
11 changed files with 142 additions and 100 deletions

0
configure_cake_wallet_android.sh Normal file → Executable file
View file

View file

@ -16,7 +16,7 @@ import 'package:cw_core/node.dart';
class NanoClient {
// bit of a hack since we need access to a node in a weird location:
static const String BACKUP_NODE_URI = "rpc.nano.to";
static const String BACKUP_NODE_URI = "rpc.nano.to:443";
static const String DEFAULT_REPRESENTATIVE =
"nano_38713x95zyjsqzx6nm1dsom1jmm668owkeb9913ax6nfgj15az3nu8xkx579";

View file

@ -27,6 +27,10 @@ class NanoUtil {
return NanoMnemomics.seedToMnemonic(seed).join(" ");
}
static Future<String> mnemonicToSeed(String mnemonic) async {
return NanoMnemomics.mnemonicListToSeed(mnemonic.split(' '));
}
// static String createPublicKey(String privateKey) {
// return NanoHelpers.byteToHex(Ed25519Blake2b.getPubkey(NanoHelpers.hexToBytes(privateKey))!);
// }
@ -60,15 +64,15 @@ class NanoUtil {
}
// // hd:
// static Future<String> hdMnemonicListToSeed(List<String> words) async {
// // if (words.length != 24) {
// // throw Exception('Expected a 24-word list, got a ${words.length} list');
// // }
// final Uint8List salt = Uint8List.fromList(utf8.encode('mnemonic'));
// final Pbkdf2 hasher = Pbkdf2(iterations: 2048);
// final String seed = await hasher.sha512(words.join(' '), salt);
// return seed;
// }
static Future<String> hdMnemonicListToSeed(List<String> words) async {
// if (words.length != 24) {
// throw Exception('Expected a 24-word list, got a ${words.length} list');
// }
final Uint8List salt = Uint8List.fromList(utf8.encode('mnemonic'));
final Pbkdf2 hasher = Pbkdf2(iterations: 2048);
final String seed = await hasher.sha512(words.join(' '), salt);
return seed;
}
static Future<String> hdSeedToPrivate(String seed, int index) async {
List<int> seedBytes = hex.decode(seed);
@ -101,6 +105,8 @@ class NanoUtil {
}
}
// static String hdSeedToPrivate(String seed, int index) {
// // List<int> seedBytes = hex.decode(seed);
// // KeyData data = await ED25519_HD_KEY.derivePath("m/44'/165'/$index'", seedBytes);

View file

@ -18,7 +18,6 @@ import 'package:cw_nano/nano_transaction_credentials.dart';
import 'package:cw_nano/nano_transaction_history.dart';
import 'package:cw_nano/nano_transaction_info.dart';
import 'package:cw_nano/nano_util.dart';
import 'package:cw_nano/nano_wallet_info.dart';
import 'package:cw_nano/nano_wallet_keys.dart';
import 'package:cw_nano/pending_nano_transaction.dart';
import 'package:mobx/mobx.dart';
@ -37,14 +36,14 @@ class NanoWallet = NanoWalletBase with _$NanoWallet;
abstract class NanoWalletBase
extends WalletBase<NanoBalance, NanoTransactionHistory, NanoTransactionInfo> with Store {
NanoWalletBase({
required NanoWalletInfo walletInfo,
required WalletInfo walletInfo,
required String mnemonic,
required String password,
NanoBalance? initialBalance,
}) : syncStatus = NotConnectedSyncStatus(),
_password = password,
_mnemonic = mnemonic,
_derivationType = walletInfo.derivationType,
_derivationType = walletInfo.derivationType!,
_isTransactionUpdating = false,
_client = NanoClient(),
walletAddresses = NanoWalletAddresses(walletInfo),
@ -88,7 +87,14 @@ abstract class NanoWalletBase
// initialize the different forms of private / public key we'll need:
Future<void> init() async {
final String type = (_derivationType == DerivationType.nano) ? "standard" : "hd";
_seedKey = bip39.mnemonicToEntropy(_mnemonic).toUpperCase();
if (_seedKey == null) {
if (_derivationType == DerivationType.nano) {
_seedKey = bip39.mnemonicToEntropy(_mnemonic).toUpperCase();
} else {
_seedKey = await NanoUtil.hdMnemonicListToSeed(_mnemonic.split(' '));
}
}
_privateKey = await NanoUtil.uniSeedToPrivate(_seedKey!, 0, type);
_publicAddress = await NanoUtil.uniSeedToAddress(_seedKey!, 0, type);
this.walletInfo.address = _publicAddress!;
@ -339,13 +345,10 @@ abstract class NanoWalletBase
derivationType = DerivationType.nano;
}
final nanoWalletInfo = NanoWalletInfo(
walletInfo: walletInfo,
derivationType: derivationType,
);
walletInfo.derivationType = derivationType;
return NanoWallet(
walletInfo: nanoWalletInfo,
walletInfo: walletInfo,
password: password,
mnemonic: mnemonic,
initialBalance: balance,

View file

@ -1,23 +0,0 @@
import 'package:cw_core/wallet_info.dart';
enum DerivationType { bip39, nano }
class NanoWalletInfo extends WalletInfo {
DerivationType derivationType;
NanoWalletInfo({required WalletInfo walletInfo, required this.derivationType})
: super(
walletInfo.id,
walletInfo.name,
walletInfo.type,
walletInfo.isRecovery,
walletInfo.restoreHeight,
walletInfo.timestamp,
walletInfo.dirPath,
walletInfo.path,
walletInfo.address,
walletInfo.yatEid,
walletInfo.yatLastUsedAddressRaw,
walletInfo.showIntroCakePayCard,
);
}

View file

@ -12,7 +12,6 @@ import 'package:cw_nano/nano_client.dart';
import 'package:cw_nano/nano_mnemonic.dart' as nm;
import 'package:cw_nano/nano_util.dart';
import 'package:cw_nano/nano_wallet.dart';
import 'package:cw_nano/nano_wallet_info.dart';
import 'package:hive/hive.dart';
import 'package:bip39/bip39.dart' as bip39;
import 'package:nanodart/nanodart.dart';
@ -75,13 +74,10 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
// derivationType derivationType = DerivationType.bip39;
// String mnemonic = bip39.generateMnemonic();
final nanoWalletInfo = NanoWalletInfo(
walletInfo: credentials.walletInfo!,
derivationType: derivationType,
);
credentials.walletInfo!.derivationType = derivationType;
final wallet = NanoWallet(
walletInfo: nanoWalletInfo,
walletInfo: credentials.walletInfo!,
mnemonic: mnemonic,
password: credentials.password!,
);
@ -109,15 +105,12 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
final currentWalletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
final NanoWalletInfo nanoWalletInfo = NanoWalletInfo(
walletInfo: currentWalletInfo,
derivationType: DerivationType.nano, // doesn't matter for rename
);
currentWalletInfo.derivationType = DerivationType.nano;// doesn't matter for the rename action
String randomWords =
(List<String>.from(nm.NanoMnemomics.WORDLIST)..shuffle()).take(24).join(' ');
final currentWallet =
NanoWallet(walletInfo: nanoWalletInfo, password: password, mnemonic: randomWords);
NanoWallet(walletInfo: currentWalletInfo, password: password, mnemonic: randomWords);
await currentWallet.renameWalletFiles(newName);
@ -128,13 +121,18 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
}
Future<DerivationType> compareDerivationMethods({String? mnemonic, String? seedKey}) async {
if (seedKey?.length == 128) {
return DerivationType.bip39;
}
static Future<DerivationType> compareDerivationMethods({String? mnemonic, String? seedKey}) async {
if (mnemonic?.split(' ').length == 12) {
return DerivationType.bip39;
}
if (seedKey?.length == 128) {
return DerivationType.bip39;
} else if (seedKey?.length == 64) {
return DerivationType.nano;
}
late String publicAddressStandard;
late String publicAddressBip39;
try {
NanoClient nanoClient = NanoClient();
@ -144,24 +142,68 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
type: WalletType.nano,
));
late String publicAddressStandard;
late String publicAddressBip39;
if (mnemonic != null) {
seedKey = await NanoUtil.hdMnemonicListToSeed(mnemonic.split(' '));
publicAddressBip39 = await NanoUtil.hdSeedToAddress(seedKey, 0);
if (seedKey == null) {
seedKey = bip39.mnemonicToEntropy(mnemonic).toUpperCase();
seedKey = await NanoUtil.mnemonicToSeed(mnemonic);
publicAddressStandard = await NanoUtil.seedToAddress(seedKey, 0);
} else if (seedKey != null) {
try {
publicAddressBip39 = await NanoUtil.hdSeedToAddress(seedKey, 0);
} catch (e) {
return DerivationType.nano;
}
try {
publicAddressStandard = await NanoUtil.seedToAddress(seedKey, 0);
} catch (e) {
return DerivationType.bip39;
}
}
// check if either has a balance:
// NanoBalance bip39Balance = await nanoClient.getBalance(publicAddressBip39);
// NanoBalance standardBalance = await nanoClient.getBalance(publicAddressStandard);
// // TODO: this is a super basic implementation, and if both addresses have balances
// // it might not be the one that the user wants, though it is unlikely
// if (bip39Balance.currentBalance > standardBalance.currentBalance) {
// return DerivationType.bip39;
// } else {
// return DerivationType.nano;
// }
// check if account has a history:
var bip39Info;
var standardInfo;
print(publicAddressBip39);
print(publicAddressStandard);
try {
bip39Info = await nanoClient.getAccountInfo(publicAddressBip39);
} catch (e) {
bip39Info = null;
}
try {
standardInfo = await nanoClient.getAccountInfo(publicAddressStandard);
} catch (e) {
standardInfo = null;
}
publicAddressBip39 = await NanoUtil.hdSeedToAddress(seedKey, 0);
publicAddressStandard = await NanoUtil.seedToAddress(seedKey, 0);
// one of these is *probably* null:
if (bip39Info == null || bip39Info["error"] != null) {
return DerivationType.nano;
} else if (standardInfo == null || standardInfo["error"] != null) {
return DerivationType.bip39;
}
// check if either has a balance:
// both are non-null:
var bip39Height = int.parse(bip39Info['confirmation_height'] as String);
var standardHeight = int.parse(standardInfo['confirmation_height'] as String);
NanoBalance bip39Balance = await nanoClient.getBalance(publicAddressBip39);
NanoBalance standardBalance = await nanoClient.getBalance(publicAddressStandard);
print(bip39Height);
print(standardHeight);
// TODO: this is a super basic implementation, and if both addresses have balances
// it might not be the one that the user wants, though it is unlikely
if (bip39Balance.currentBalance > standardBalance.currentBalance) {
if (bip39Height > standardHeight) {
return DerivationType.bip39;
} else {
return DerivationType.nano;
@ -207,15 +249,12 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
DerivationType derivationType = credentials.derivationType ??
await compareDerivationMethods(mnemonic: credentials.mnemonic);
final nanoWalletInfo = NanoWalletInfo(
walletInfo: credentials.walletInfo!,
derivationType: derivationType,
);
credentials.walletInfo!.derivationType = derivationType;
final wallet = await NanoWallet(
password: credentials.password!,
mnemonic: credentials.mnemonic,
walletInfo: nanoWalletInfo,
walletInfo: credentials.walletInfo!,
);
await wallet.init();

View file

@ -91,6 +91,10 @@ Future<void> initializeAppConfigs() async {
Hive.registerAdapter(WalletInfoAdapter());
}
if (!Hive.isAdapterRegistered(derivationTypeTypeId)) {
Hive.registerAdapter(DerivationTypeAdapter());
}
if (!Hive.isAdapterRegistered(walletTypeTypeId)) {
Hive.registerAdapter(WalletTypeAdapter());
}

View file

@ -108,13 +108,24 @@ class CWNano extends Nano {
required String password,
required String mnemonic,
DerivationType? derivationType,
}) =>
NanoRestoreWalletFromSeedCredentials(
name: name,
password: password,
mnemonic: mnemonic,
derivationType: derivationType,
);
}) {
if (derivationType == null) {
// figure out the derivation type as best we can, otherwise set it to "unknown"
if (mnemonic.split(" ").length == 12) {
derivationType = DerivationType.bip39;
} else {
derivationType = DerivationType.unknown;
}
}
return NanoRestoreWalletFromSeedCredentials(
name: name,
password: password,
mnemonic: mnemonic,
derivationType: derivationType,
);
}
@override
TransactionHistoryBase getTransactionHistory(Object wallet) {

View file

@ -1,3 +1,4 @@
import 'package:cw_core/nano_account.dart';
import 'package:cw_nano/nano_mnemonic.dart';
import 'package:cw_nano/nano_wallet.dart';
@ -6,12 +7,14 @@ import 'package:cake_wallet/view_model/send/output.dart';
import 'package:cw_core/account.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_nano/nano_wallet_info.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/output_info.dart';
import 'package:hive/hive.dart';
import 'package:cw_nano/api/wallet.dart' as nano_wallet_api;
import 'package:cw_nano/nano_balance.dart';
import 'package:cw_nano/nano_wallet_creation_credentials.dart';
import 'package:cw_nano/nano_transaction_credentials.dart';
part 'cw_nano.dart';
@ -33,7 +36,7 @@ abstract class Nano {
required String name,
String password,
});
WalletCredentials createNanoRestoreWalletFromSeedCredentials({
required String name,
required String password,
@ -58,3 +61,4 @@ abstract class NanoAccountList {
Future<void> addAccount(Object wallet, {required String label});
Future<void> setLabelAccount(Object wallet, {required int accountIndex, required String label});
}

View file

@ -1,6 +1,7 @@
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
@ -91,8 +92,7 @@ class WalletRestorePage extends BasePage {
fontSize: 18.0,
fontWeight: FontWeight.bold,
fontFamily: 'Lato',
color: titleColor ??
Theme.of(context).primaryTextTheme!.titleLarge!.color!),
color: titleColor ?? Theme.of(context).primaryTextTheme!.titleLarge!.color!),
));
final WalletRestoreViewModel walletRestoreViewModel;
@ -139,10 +139,7 @@ class WalletRestorePage extends BasePage {
return KeyboardActions(
config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
keyboardBarColor: Theme.of(context)
.accentTextTheme!
.bodyLarge!
.backgroundColor!,
keyboardBarColor: Theme.of(context).accentTextTheme!.bodyLarge!.backgroundColor!,
nextFocus: false,
actions: [
KeyboardActionsItem(
@ -192,16 +189,13 @@ class WalletRestorePage extends BasePage {
child: Observer(
builder: (context) {
return LoadingPrimaryButton(
onPressed: _confirmForm,
onPressed: () async {
_confirmForm(context);
},
text: S.of(context).restore_recover,
color: Theme.of(context)
.accentTextTheme!
.titleSmall!
.decorationColor!,
textColor: Theme.of(context)
.accentTextTheme!
.headlineSmall!
.decorationColor!,
color: Theme.of(context).accentTextTheme!.titleSmall!.decorationColor!,
textColor:
Theme.of(context).accentTextTheme!.headlineSmall!.decorationColor!,
isLoading: walletRestoreViewModel.state is IsExecutingState,
isDisabled: !walletRestoreViewModel.isButtonEnabled,
);
@ -265,7 +259,7 @@ class WalletRestorePage extends BasePage {
return credentials;
}
void _confirmForm() {
void _confirmForm(BuildContext context) {
// Dismissing all visible keyboard to provide context for navigation
FocusManager.instance.primaryFocus?.unfocus();
final formContext = walletRestoreViewModel.mode == WalletRestoreMode.seed
@ -289,6 +283,11 @@ class WalletRestorePage extends BasePage {
return;
}
var credentials = walletRestoreViewModel.getCredentials(_credentials());
if (credentials.walletInfo?.derivationType == DerivationType.unknown) {
}
walletRestoreViewModel.create(options: _credentials());
}

View file

@ -2,7 +2,6 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/nano/nano.dart';
import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cw_nano/nano_wallet.dart';
import 'package:cw_nano/nano_wallet_info.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/store/app_store.dart';