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 { class NanoClient {
// bit of a hack since we need access to a node in a weird location: // 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 = static const String DEFAULT_REPRESENTATIVE =
"nano_38713x95zyjsqzx6nm1dsom1jmm668owkeb9913ax6nfgj15az3nu8xkx579"; "nano_38713x95zyjsqzx6nm1dsom1jmm668owkeb9913ax6nfgj15az3nu8xkx579";

View file

@ -27,6 +27,10 @@ class NanoUtil {
return NanoMnemomics.seedToMnemonic(seed).join(" "); return NanoMnemomics.seedToMnemonic(seed).join(" ");
} }
static Future<String> mnemonicToSeed(String mnemonic) async {
return NanoMnemomics.mnemonicListToSeed(mnemonic.split(' '));
}
// static String createPublicKey(String privateKey) { // static String createPublicKey(String privateKey) {
// return NanoHelpers.byteToHex(Ed25519Blake2b.getPubkey(NanoHelpers.hexToBytes(privateKey))!); // return NanoHelpers.byteToHex(Ed25519Blake2b.getPubkey(NanoHelpers.hexToBytes(privateKey))!);
// } // }
@ -60,15 +64,15 @@ class NanoUtil {
} }
// // hd: // // hd:
// static Future<String> hdMnemonicListToSeed(List<String> words) async { static Future<String> hdMnemonicListToSeed(List<String> words) async {
// // if (words.length != 24) { // if (words.length != 24) {
// // throw Exception('Expected a 24-word list, got a ${words.length} list'); // throw Exception('Expected a 24-word list, got a ${words.length} list');
// // } // }
// final Uint8List salt = Uint8List.fromList(utf8.encode('mnemonic')); final Uint8List salt = Uint8List.fromList(utf8.encode('mnemonic'));
// final Pbkdf2 hasher = Pbkdf2(iterations: 2048); final Pbkdf2 hasher = Pbkdf2(iterations: 2048);
// final String seed = await hasher.sha512(words.join(' '), salt); final String seed = await hasher.sha512(words.join(' '), salt);
// return seed; return seed;
// } }
static Future<String> hdSeedToPrivate(String seed, int index) async { static Future<String> hdSeedToPrivate(String seed, int index) async {
List<int> seedBytes = hex.decode(seed); List<int> seedBytes = hex.decode(seed);
@ -101,6 +105,8 @@ class NanoUtil {
} }
} }
// static String hdSeedToPrivate(String seed, int index) { // static String hdSeedToPrivate(String seed, int index) {
// // List<int> seedBytes = hex.decode(seed); // // List<int> seedBytes = hex.decode(seed);
// // KeyData data = await ED25519_HD_KEY.derivePath("m/44'/165'/$index'", seedBytes); // // 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_history.dart';
import 'package:cw_nano/nano_transaction_info.dart'; import 'package:cw_nano/nano_transaction_info.dart';
import 'package:cw_nano/nano_util.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/nano_wallet_keys.dart';
import 'package:cw_nano/pending_nano_transaction.dart'; import 'package:cw_nano/pending_nano_transaction.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
@ -37,14 +36,14 @@ class NanoWallet = NanoWalletBase with _$NanoWallet;
abstract class NanoWalletBase abstract class NanoWalletBase
extends WalletBase<NanoBalance, NanoTransactionHistory, NanoTransactionInfo> with Store { extends WalletBase<NanoBalance, NanoTransactionHistory, NanoTransactionInfo> with Store {
NanoWalletBase({ NanoWalletBase({
required NanoWalletInfo walletInfo, required WalletInfo walletInfo,
required String mnemonic, required String mnemonic,
required String password, required String password,
NanoBalance? initialBalance, NanoBalance? initialBalance,
}) : syncStatus = NotConnectedSyncStatus(), }) : syncStatus = NotConnectedSyncStatus(),
_password = password, _password = password,
_mnemonic = mnemonic, _mnemonic = mnemonic,
_derivationType = walletInfo.derivationType, _derivationType = walletInfo.derivationType!,
_isTransactionUpdating = false, _isTransactionUpdating = false,
_client = NanoClient(), _client = NanoClient(),
walletAddresses = NanoWalletAddresses(walletInfo), walletAddresses = NanoWalletAddresses(walletInfo),
@ -88,7 +87,14 @@ abstract class NanoWalletBase
// initialize the different forms of private / public key we'll need: // initialize the different forms of private / public key we'll need:
Future<void> init() async { Future<void> init() async {
final String type = (_derivationType == DerivationType.nano) ? "standard" : "hd"; 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); _privateKey = await NanoUtil.uniSeedToPrivate(_seedKey!, 0, type);
_publicAddress = await NanoUtil.uniSeedToAddress(_seedKey!, 0, type); _publicAddress = await NanoUtil.uniSeedToAddress(_seedKey!, 0, type);
this.walletInfo.address = _publicAddress!; this.walletInfo.address = _publicAddress!;
@ -339,13 +345,10 @@ abstract class NanoWalletBase
derivationType = DerivationType.nano; derivationType = DerivationType.nano;
} }
final nanoWalletInfo = NanoWalletInfo( walletInfo.derivationType = derivationType;
walletInfo: walletInfo,
derivationType: derivationType,
);
return NanoWallet( return NanoWallet(
walletInfo: nanoWalletInfo, walletInfo: walletInfo,
password: password, password: password,
mnemonic: mnemonic, mnemonic: mnemonic,
initialBalance: balance, 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_mnemonic.dart' as nm;
import 'package:cw_nano/nano_util.dart'; import 'package:cw_nano/nano_util.dart';
import 'package:cw_nano/nano_wallet.dart'; import 'package:cw_nano/nano_wallet.dart';
import 'package:cw_nano/nano_wallet_info.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:bip39/bip39.dart' as bip39; import 'package:bip39/bip39.dart' as bip39;
import 'package:nanodart/nanodart.dart'; import 'package:nanodart/nanodart.dart';
@ -75,13 +74,10 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
// derivationType derivationType = DerivationType.bip39; // derivationType derivationType = DerivationType.bip39;
// String mnemonic = bip39.generateMnemonic(); // String mnemonic = bip39.generateMnemonic();
final nanoWalletInfo = NanoWalletInfo( credentials.walletInfo!.derivationType = derivationType;
walletInfo: credentials.walletInfo!,
derivationType: derivationType,
);
final wallet = NanoWallet( final wallet = NanoWallet(
walletInfo: nanoWalletInfo, walletInfo: credentials.walletInfo!,
mnemonic: mnemonic, mnemonic: mnemonic,
password: credentials.password!, password: credentials.password!,
); );
@ -109,15 +105,12 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
final currentWalletInfo = walletInfoSource.values final currentWalletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType())); .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
final NanoWalletInfo nanoWalletInfo = NanoWalletInfo( currentWalletInfo.derivationType = DerivationType.nano;// doesn't matter for the rename action
walletInfo: currentWalletInfo,
derivationType: DerivationType.nano, // doesn't matter for rename
);
String randomWords = String randomWords =
(List<String>.from(nm.NanoMnemomics.WORDLIST)..shuffle()).take(24).join(' '); (List<String>.from(nm.NanoMnemomics.WORDLIST)..shuffle()).take(24).join(' ');
final currentWallet = final currentWallet =
NanoWallet(walletInfo: nanoWalletInfo, password: password, mnemonic: randomWords); NanoWallet(walletInfo: currentWalletInfo, password: password, mnemonic: randomWords);
await currentWallet.renameWalletFiles(newName); await currentWallet.renameWalletFiles(newName);
@ -128,13 +121,18 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo); await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
} }
Future<DerivationType> compareDerivationMethods({String? mnemonic, String? seedKey}) async { static Future<DerivationType> compareDerivationMethods({String? mnemonic, String? seedKey}) async {
if (seedKey?.length == 128) {
return DerivationType.bip39;
}
if (mnemonic?.split(' ').length == 12) { if (mnemonic?.split(' ').length == 12) {
return DerivationType.bip39; 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 { try {
NanoClient nanoClient = NanoClient(); NanoClient nanoClient = NanoClient();
@ -144,24 +142,68 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
type: WalletType.nano, type: WalletType.nano,
)); ));
late String publicAddressStandard; if (mnemonic != null) {
late String publicAddressBip39; seedKey = await NanoUtil.hdMnemonicListToSeed(mnemonic.split(' '));
publicAddressBip39 = await NanoUtil.hdSeedToAddress(seedKey, 0);
if (seedKey == null) { seedKey = await NanoUtil.mnemonicToSeed(mnemonic);
seedKey = bip39.mnemonicToEntropy(mnemonic).toUpperCase(); 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); // one of these is *probably* null:
publicAddressStandard = await NanoUtil.seedToAddress(seedKey, 0); 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); print(bip39Height);
NanoBalance standardBalance = await nanoClient.getBalance(publicAddressStandard); print(standardHeight);
// TODO: this is a super basic implementation, and if both addresses have balances if (bip39Height > standardHeight) {
// it might not be the one that the user wants, though it is unlikely
if (bip39Balance.currentBalance > standardBalance.currentBalance) {
return DerivationType.bip39; return DerivationType.bip39;
} else { } else {
return DerivationType.nano; return DerivationType.nano;
@ -207,15 +249,12 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
DerivationType derivationType = credentials.derivationType ?? DerivationType derivationType = credentials.derivationType ??
await compareDerivationMethods(mnemonic: credentials.mnemonic); await compareDerivationMethods(mnemonic: credentials.mnemonic);
final nanoWalletInfo = NanoWalletInfo( credentials.walletInfo!.derivationType = derivationType;
walletInfo: credentials.walletInfo!,
derivationType: derivationType,
);
final wallet = await NanoWallet( final wallet = await NanoWallet(
password: credentials.password!, password: credentials.password!,
mnemonic: credentials.mnemonic, mnemonic: credentials.mnemonic,
walletInfo: nanoWalletInfo, walletInfo: credentials.walletInfo!,
); );
await wallet.init(); await wallet.init();

View file

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

View file

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

View file

@ -1,3 +1,4 @@
import 'package:cw_core/nano_account.dart'; import 'package:cw_core/nano_account.dart';
import 'package:cw_nano/nano_mnemonic.dart'; import 'package:cw_nano/nano_mnemonic.dart';
import 'package:cw_nano/nano_wallet.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:cw_core/account.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cw_core/wallet_credentials.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/wallet_info.dart';
import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/output_info.dart'; import 'package:cw_core/output_info.dart';
import 'package:hive/hive.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'; import 'package:cw_nano/nano_transaction_credentials.dart';
part 'cw_nano.dart'; part 'cw_nano.dart';
@ -33,7 +36,7 @@ abstract class Nano {
required String name, required String name,
String password, String password,
}); });
WalletCredentials createNanoRestoreWalletFromSeedCredentials({ WalletCredentials createNanoRestoreWalletFromSeedCredentials({
required String name, required String name,
required String password, required String password,
@ -58,3 +61,4 @@ abstract class NanoAccountList {
Future<void> addAccount(Object wallet, {required String label}); Future<void> addAccount(Object wallet, {required String label});
Future<void> setLabelAccount(Object wallet, {required int accountIndex, 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/keyboard_done_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/utils/responsive_layout_util.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:cw_core/wallet_type.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -91,8 +92,7 @@ class WalletRestorePage extends BasePage {
fontSize: 18.0, fontSize: 18.0,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontFamily: 'Lato', fontFamily: 'Lato',
color: titleColor ?? color: titleColor ?? Theme.of(context).primaryTextTheme!.titleLarge!.color!),
Theme.of(context).primaryTextTheme!.titleLarge!.color!),
)); ));
final WalletRestoreViewModel walletRestoreViewModel; final WalletRestoreViewModel walletRestoreViewModel;
@ -139,10 +139,7 @@ class WalletRestorePage extends BasePage {
return KeyboardActions( return KeyboardActions(
config: KeyboardActionsConfig( config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS, keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
keyboardBarColor: Theme.of(context) keyboardBarColor: Theme.of(context).accentTextTheme!.bodyLarge!.backgroundColor!,
.accentTextTheme!
.bodyLarge!
.backgroundColor!,
nextFocus: false, nextFocus: false,
actions: [ actions: [
KeyboardActionsItem( KeyboardActionsItem(
@ -192,16 +189,13 @@ class WalletRestorePage extends BasePage {
child: Observer( child: Observer(
builder: (context) { builder: (context) {
return LoadingPrimaryButton( return LoadingPrimaryButton(
onPressed: _confirmForm, onPressed: () async {
_confirmForm(context);
},
text: S.of(context).restore_recover, text: S.of(context).restore_recover,
color: Theme.of(context) color: Theme.of(context).accentTextTheme!.titleSmall!.decorationColor!,
.accentTextTheme! textColor:
.titleSmall! Theme.of(context).accentTextTheme!.headlineSmall!.decorationColor!,
.decorationColor!,
textColor: Theme.of(context)
.accentTextTheme!
.headlineSmall!
.decorationColor!,
isLoading: walletRestoreViewModel.state is IsExecutingState, isLoading: walletRestoreViewModel.state is IsExecutingState,
isDisabled: !walletRestoreViewModel.isButtonEnabled, isDisabled: !walletRestoreViewModel.isButtonEnabled,
); );
@ -265,7 +259,7 @@ class WalletRestorePage extends BasePage {
return credentials; return credentials;
} }
void _confirmForm() { void _confirmForm(BuildContext context) {
// Dismissing all visible keyboard to provide context for navigation // Dismissing all visible keyboard to provide context for navigation
FocusManager.instance.primaryFocus?.unfocus(); FocusManager.instance.primaryFocus?.unfocus();
final formContext = walletRestoreViewModel.mode == WalletRestoreMode.seed final formContext = walletRestoreViewModel.mode == WalletRestoreMode.seed
@ -289,6 +283,11 @@ class WalletRestorePage extends BasePage {
return; return;
} }
var credentials = walletRestoreViewModel.getCredentials(_credentials());
if (credentials.walletInfo?.derivationType == DerivationType.unknown) {
}
walletRestoreViewModel.create(options: _credentials()); 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/nano/nano.dart';
import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cw_nano/nano_wallet.dart'; import 'package:cw_nano/nano_wallet.dart';
import 'package:cw_nano/nano_wallet_info.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/store/app_store.dart';