mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-23 12:09:43 +00:00
seed generation
This commit is contained in:
parent
7f93f816de
commit
2670eb2868
14 changed files with 202 additions and 55 deletions
4
assets/nano_node_list.yml
Normal file
4
assets/nano_node_list.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
-
|
||||
uri: rpc.nano.to
|
||||
-
|
||||
uri: eth.llamarpc.com
|
39
cw_nano/lib/file.dart
Normal file
39
cw_nano/lib/file.dart
Normal file
|
@ -0,0 +1,39 @@
|
|||
import 'dart:io';
|
||||
import 'package:cw_core/key.dart';
|
||||
import 'package:encrypt/encrypt.dart' as encrypt;
|
||||
|
||||
Future<void> write(
|
||||
{required String path,
|
||||
required String password,
|
||||
required String data}) async {
|
||||
final keys = extractKeys(password);
|
||||
final key = encrypt.Key.fromBase64(keys.first);
|
||||
final iv = encrypt.IV.fromBase64(keys.last);
|
||||
final encrypted = await encode(key: key, iv: iv, data: data);
|
||||
final f = File(path);
|
||||
f.writeAsStringSync(encrypted);
|
||||
}
|
||||
|
||||
Future<void> writeData(
|
||||
{required String path,
|
||||
required String password,
|
||||
required String data}) async {
|
||||
final keys = extractKeys(password);
|
||||
final key = encrypt.Key.fromBase64(keys.first);
|
||||
final iv = encrypt.IV.fromBase64(keys.last);
|
||||
final encrypted = await encode(key: key, iv: iv, data: data);
|
||||
final f = File(path);
|
||||
f.writeAsStringSync(encrypted);
|
||||
}
|
||||
|
||||
Future<String> read({required String path, required String password}) async {
|
||||
final file = File(path);
|
||||
|
||||
if (!file.existsSync()) {
|
||||
file.createSync();
|
||||
}
|
||||
|
||||
final encrypted = file.readAsStringSync();
|
||||
|
||||
return decode(password: password, data: encrypted);
|
||||
}
|
|
@ -1,6 +1,12 @@
|
|||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:nanodart/nanodart.dart';
|
||||
|
||||
class NanoMnemonicIsIncorrectException implements Exception {
|
||||
@override
|
||||
String toString() =>
|
||||
'Nano mnemonic has incorrect format. Mnemonic should contain 12 or 24 words separated by space.';
|
||||
}
|
||||
|
||||
class NanoMnemomics {
|
||||
/// Converts a nano seed to a 24-word mnemonic word list
|
||||
static List<String> seedToMnemonic(String seed) {
|
||||
|
|
|
@ -6,15 +6,20 @@ import "package:ed25519_hd_key/ed25519_hd_key.dart";
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:libcrypto/libcrypto.dart';
|
||||
import 'package:nanodart/nanodart.dart';
|
||||
import 'package:ed25519_hd_key/ed25519_hd_key.dart';
|
||||
import 'package:nanodart/nanodart.dart';
|
||||
|
||||
class NanoUtil {
|
||||
// standard:
|
||||
static String seedToPrivate(String seed, int index) {
|
||||
return NanoHelpers.byteToHex(Ed25519Blake2b.derivePrivkey(NanoHelpers.hexToBytes(seed), index)!).toUpperCase();
|
||||
// return NanoHelpers.byteToHex(Ed25519Blake2b.derivePrivkey(NanoHelpers.hexToBytes(seed), index)!)
|
||||
// .toUpperCase();
|
||||
return NanoKeys.seedToPrivate(seed, index);
|
||||
}
|
||||
|
||||
static String seedToAddress(String seed, int index) {
|
||||
return NanoAccounts.createAccount(NanoAccountType.NANO, privateKeyToPublic(seedToPrivate(seed, index)));
|
||||
return NanoAccounts.createAccount(
|
||||
NanoAccountType.NANO, privateKeyToPublic(seedToPrivate(seed, index)));
|
||||
}
|
||||
|
||||
// static String createPublicKey(String privateKey) {
|
||||
|
@ -22,7 +27,8 @@ class NanoUtil {
|
|||
// }
|
||||
|
||||
static String privateKeyToPublic(String privateKey) {
|
||||
return NanoHelpers.byteToHex(Ed25519Blake2b.getPubkey(NanoHelpers.hexToBytes(privateKey))!);
|
||||
// return NanoHelpers.byteToHex(Ed25519Blake2b.getPubkey(NanoHelpers.hexToBytes(privateKey))!);
|
||||
return NanoKeys.createPublicKey(privateKey);
|
||||
}
|
||||
|
||||
static String addressToPublicKey(String publicAddress) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
|
@ -9,32 +8,37 @@ import 'package:cw_core/sync_status.dart';
|
|||
import 'package:cw_core/transaction_priority.dart';
|
||||
import 'package:cw_core/wallet_addresses.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_nano/file.dart';
|
||||
import 'package:cw_nano/nano_balance.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:mobx/mobx.dart';
|
||||
import 'package:web3dart/credentials.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:cw_nano/nano_wallet_addresses.dart';
|
||||
import 'package:cw_nano/nano_wallet_keys.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:web3dart/web3dart.dart';
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:bip32/bip32.dart' as bip32;
|
||||
|
||||
part 'nano_wallet.g.dart';
|
||||
|
||||
class NanoWallet = NanoWalletBase with _$NanoWallet;
|
||||
|
||||
enum DerivationType { bip39, nano }
|
||||
|
||||
abstract class NanoWalletBase
|
||||
extends WalletBase<NanoBalance, NanoTransactionHistory, NanoTransactionInfo> with Store {
|
||||
NanoWalletBase({
|
||||
required WalletInfo walletInfo,
|
||||
required String mnemonic,
|
||||
required String password,
|
||||
required DerivationType derivationType,
|
||||
NanoBalance? initialBalance,
|
||||
}) : syncStatus = NotConnectedSyncStatus(),
|
||||
_password = password,
|
||||
_mnemonic = mnemonic,
|
||||
_derivationType = derivationType,
|
||||
_isTransactionUpdating = false,
|
||||
_priorityFees = [],
|
||||
walletAddresses = NanoWalletAddresses(walletInfo),
|
||||
|
@ -43,13 +47,17 @@ abstract class NanoWalletBase
|
|||
NanoBalance(currentBalance: BigInt.zero, receivableBalance: BigInt.zero)
|
||||
}),
|
||||
super(walletInfo) {
|
||||
print("@@@@@ initializing nano wallet @@@@@");
|
||||
this.walletInfo = walletInfo;
|
||||
transactionHistory = NanoTransactionHistory();
|
||||
}
|
||||
|
||||
final String _mnemonic;
|
||||
final String _password;
|
||||
final DerivationType _derivationType;
|
||||
|
||||
late final String _privateKey;
|
||||
late final String _publicAddress;
|
||||
late final String _seed;
|
||||
|
||||
List<int> _priorityFees;
|
||||
int? _gasPrice;
|
||||
|
@ -66,11 +74,25 @@ abstract class NanoWalletBase
|
|||
@observable
|
||||
late ObservableMap<CryptoCurrency, NanoBalance> balance;
|
||||
|
||||
Future<void> init() async {}
|
||||
|
||||
// initialize the different forms of private / public key we'll need:
|
||||
Future<void> init() async {
|
||||
final String type = (_derivationType == DerivationType.nano) ? "standard" : "hd";
|
||||
|
||||
_seed = bip39.mnemonicToEntropy(_mnemonic).toUpperCase();
|
||||
_privateKey = await NanoUtil.uniSeedToPrivate(_mnemonic, 0, type);
|
||||
_publicAddress = await NanoUtil.uniSeedToAddress(_mnemonic, 0, type);
|
||||
|
||||
await walletAddresses.init();
|
||||
// await transactionHistory.init();
|
||||
|
||||
// walletAddresses.address = _privateKey.address.toString();
|
||||
await save();
|
||||
}
|
||||
|
||||
@override
|
||||
int calculateEstimatedFee(TransactionPriority priority, int? amount) {
|
||||
return 0;
|
||||
return 0; // always 0 :)
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -122,8 +144,10 @@ abstract class NanoWalletBase
|
|||
|
||||
@override
|
||||
Future<void> save() async {
|
||||
print("l");
|
||||
throw UnimplementedError();
|
||||
await walletAddresses.updateAddressesInBox();
|
||||
final path = await makePath();
|
||||
await write(path: path, password: _password, data: toJSON());
|
||||
await transactionHistory.save();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -139,6 +163,13 @@ abstract class NanoWalletBase
|
|||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type);
|
||||
|
||||
String toJSON() => json.encode({
|
||||
'mnemonic': _mnemonic,
|
||||
// 'balance': balance[currency]!.toJSON(),
|
||||
});
|
||||
|
||||
static Future<NanoWallet> open({
|
||||
required String name,
|
||||
required String password,
|
||||
|
@ -151,10 +182,6 @@ abstract class NanoWalletBase
|
|||
await save();
|
||||
}
|
||||
|
||||
Future<void> _fetchErc20Balances() async {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
Future<EthPrivateKey> getPrivateKey(String mnemonic, String password) async {
|
||||
print("o");
|
||||
throw UnimplementedError();
|
||||
|
@ -162,14 +189,6 @@ abstract class NanoWalletBase
|
|||
|
||||
Future<void>? updateBalance() async => await _updateBalance();
|
||||
|
||||
Future<void> addErc20Token(Erc20Token token) async {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
Future<void> deleteErc20Token(Erc20Token token) async {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
void _onNewTransaction(FilterEvent event) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:cw_core/wallet_credentials.dart';
|
|||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_service.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_nano/nano_mnemonic.dart';
|
||||
// import 'package:cw_nano/nano_mnemonics.dart';
|
||||
import 'package:cw_nano/nano_wallet.dart';
|
||||
import 'package:cw_nano/nano_wallet_creation_credentials.dart';
|
||||
|
@ -19,10 +20,15 @@ class NanoNewWalletCredentials extends WalletCredentials {
|
|||
|
||||
class NanoRestoreWalletFromSeedCredentials extends WalletCredentials {
|
||||
NanoRestoreWalletFromSeedCredentials(
|
||||
{required String name, required this.mnemonic, int height = 0, String? password})
|
||||
{required String name,
|
||||
required this.mnemonic,
|
||||
required this.derivationType,
|
||||
int height = 0,
|
||||
String? password})
|
||||
: super(name: name, password: password, height: height);
|
||||
|
||||
final String mnemonic;
|
||||
final DerivationType derivationType;
|
||||
}
|
||||
|
||||
class NanoWalletLoadingException implements Exception {
|
||||
|
@ -67,6 +73,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
|||
walletInfo: credentials.walletInfo!,
|
||||
mnemonic: mnemonic,
|
||||
password: credentials.password!,
|
||||
derivationType: DerivationType.bip39,
|
||||
);
|
||||
return wallet;
|
||||
}
|
||||
|
@ -126,37 +133,63 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
|||
// }
|
||||
}
|
||||
|
||||
Future<DerivationType> compareDerivationMethods({String? mnemonic, String? seedKey}) async {
|
||||
return DerivationType.nano;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<NanoWallet> restoreFromSeed(NanoRestoreWalletFromSeedCredentials credentials) async {
|
||||
print("b");
|
||||
throw UnimplementedError();
|
||||
// try {
|
||||
// final path = await pathForWallet(name: credentials.name, type: getType());
|
||||
// await monero_wallet_manager.restoreFromSeed(
|
||||
// path: path,
|
||||
// password: credentials.password!,
|
||||
// seed: credentials.mnemonic,
|
||||
// restoreHeight: credentials.height!);
|
||||
// final wallet = NanoWallet(walletInfo: credentials.walletInfo!);
|
||||
// await wallet.init();
|
||||
if (!bip39.validateMnemonic(credentials.mnemonic)) {
|
||||
throw NanoMnemonicIsIncorrectException();
|
||||
}
|
||||
|
||||
// return wallet;
|
||||
// } catch (e) {
|
||||
// // TODO: Implement Exception for wallet list service.
|
||||
// print('NanoWalletsManager Error: $e');
|
||||
// rethrow;
|
||||
// }
|
||||
if (!NanoMnemomics.validateMnemonic(credentials.mnemonic.split(' '))) {
|
||||
throw NanoMnemonicIsIncorrectException();
|
||||
}
|
||||
|
||||
DerivationType derivationType = DerivationType.bip39;
|
||||
|
||||
if (credentials.mnemonic.split(' ').length == 12) {
|
||||
derivationType = DerivationType.bip39;
|
||||
} else {
|
||||
// we don't know for sure, but probably the nano standard:
|
||||
derivationType = await compareDerivationMethods(mnemonic: credentials.mnemonic);
|
||||
}
|
||||
|
||||
final wallet = await NanoWallet(
|
||||
password: credentials.password!,
|
||||
mnemonic: credentials.mnemonic,
|
||||
walletInfo: credentials.walletInfo!,
|
||||
derivationType: derivationType,
|
||||
);
|
||||
|
||||
try {
|
||||
await wallet.init();
|
||||
} catch (e) {
|
||||
print("test");
|
||||
print(e);
|
||||
rethrow;
|
||||
}
|
||||
await wallet.save();
|
||||
return wallet;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> isWalletExit(String s) async {
|
||||
print("c");
|
||||
throw UnimplementedError();
|
||||
}
|
||||
Future<bool> isWalletExit(String name) async =>
|
||||
File(await pathForWallet(name: name, type: getType())).existsSync();
|
||||
|
||||
@override
|
||||
Future<WalletBase> openWallet(String s, String s2) async {
|
||||
print("d");
|
||||
throw UnimplementedError();
|
||||
Future<NanoWallet> openWallet(String name, String password) async {
|
||||
final walletInfo =
|
||||
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
|
||||
final wallet = await NanoWalletBase.open(
|
||||
name: name,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
);
|
||||
|
||||
await wallet.init();
|
||||
await wallet.save();
|
||||
return wallet;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ const cakeWalletBitcoinElectrumUri = 'electrum.cakewallet.com:50002';
|
|||
const cakeWalletLitecoinElectrumUri = 'ltc-electrum.cakewallet.com:50002';
|
||||
const havenDefaultNodeUri = 'nodes.havenprotocol.org:443';
|
||||
const ethereumDefaultNodeUri = 'ethereum.publicnode.com';
|
||||
const nanoDefaultNodeUri = 'rpc.nano.to';
|
||||
|
||||
Future defaultSettingsMigration(
|
||||
{required int version,
|
||||
|
@ -255,6 +256,12 @@ Node? getEthereumDefaultNode({required Box<Node> nodes}) {
|
|||
?? nodes.values.firstWhereOrNull((node) => node.type == WalletType.ethereum);
|
||||
}
|
||||
|
||||
Node? getNanoDefaultNode({required Box<Node> nodes}) {
|
||||
return nodes.values.firstWhereOrNull(
|
||||
(Node node) => node.uriRaw == nanoDefaultNodeUri)
|
||||
?? nodes.values.firstWhereOrNull((node) => node.type == WalletType.ethereum);
|
||||
}
|
||||
|
||||
Node getMoneroDefaultNode({required Box<Node> nodes}) {
|
||||
final timeZone = DateTime.now().timeZoneOffset.inHours;
|
||||
var nodeUri = '';
|
||||
|
|
|
@ -86,16 +86,34 @@ Future<List<Node>> loadDefaultEthereumNodes() async {
|
|||
return nodes;
|
||||
}
|
||||
|
||||
Future<List<Node>> loadDefaultNanoNodes() async {
|
||||
final nodesRaw = await rootBundle.loadString('assets/nano_node_list.yml');
|
||||
final loadedNodes = loadYaml(nodesRaw) as YamlList;
|
||||
final nodes = <Node>[];
|
||||
|
||||
for (final raw in loadedNodes) {
|
||||
if (raw is Map) {
|
||||
final node = Node.fromMap(Map<String, Object>.from(raw));
|
||||
node.type = WalletType.ethereum;
|
||||
nodes.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
Future resetToDefault(Box<Node> nodeSource) async {
|
||||
final moneroNodes = await loadDefaultNodes();
|
||||
final bitcoinElectrumServerList = await loadBitcoinElectrumServerList();
|
||||
final litecoinElectrumServerList = await loadLitecoinElectrumServerList();
|
||||
final havenNodes = await loadDefaultHavenNodes();
|
||||
final nanoNodes = await loadDefaultNanoNodes();
|
||||
final nodes =
|
||||
moneroNodes +
|
||||
bitcoinElectrumServerList +
|
||||
litecoinElectrumServerList +
|
||||
havenNodes;
|
||||
havenNodes +
|
||||
nanoNodes;
|
||||
|
||||
await nodeSource.clear();
|
||||
await nodeSource.addAll(nodes);
|
||||
|
|
|
@ -40,12 +40,14 @@ class CWNano extends Nano {
|
|||
WalletCredentials createNanoRestoreWalletFromSeedCredentials({
|
||||
required String name,
|
||||
required String mnemonic,
|
||||
required DerivationType derivationType,
|
||||
required String password,
|
||||
}) =>
|
||||
NanoRestoreWalletFromSeedCredentials(
|
||||
name: name,
|
||||
password: password,
|
||||
mnemonic: mnemonic,
|
||||
derivationType: derivationType,
|
||||
);
|
||||
|
||||
@override
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:cw_nano/nano_wallet.dart';
|
||||
import 'package:cw_nano/nano_wallet_service.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cw_core/wallet_credentials.dart';
|
||||
|
@ -52,6 +53,7 @@ abstract class Nano {
|
|||
required String name,
|
||||
required String mnemonic,
|
||||
required String password,
|
||||
required DerivationType derivationType,
|
||||
});
|
||||
|
||||
String getTransactionAddress(Object wallet, int accountIndex, int addressIndex);
|
||||
|
|
|
@ -63,6 +63,9 @@ abstract class NodeListViewModelBase with Store {
|
|||
case WalletType.haven:
|
||||
node = getHavenDefaultNode(nodes: _nodeSource)!;
|
||||
break;
|
||||
case WalletType.nano:
|
||||
node = getNanoDefaultNode(nodes: _nodeSource)!;
|
||||
break;
|
||||
default:
|
||||
throw Exception('Unexpected wallet type: ${_appStore.wallet!.type}');
|
||||
}
|
||||
|
|
|
@ -47,9 +47,9 @@ abstract class WalletRestorationFromSeedVMBase extends WalletCreationVM with Sto
|
|||
case WalletType.bitcoin:
|
||||
return bitcoin!.createBitcoinRestoreWalletFromSeedCredentials(
|
||||
name: name, mnemonic: seed, password: password);
|
||||
case WalletType.nano:
|
||||
return nano!.createNanoRestoreWalletFromSeedCredentials(
|
||||
name: name, mnemonic: seed, password: password);
|
||||
// case WalletType.nano:
|
||||
// return nano!.createNanoRestoreWalletFromSeedCredentials(
|
||||
// name: name, mnemonic: seed, password: password, derivationType: );
|
||||
default:
|
||||
throw Exception('Unexpected type: ${type.toString()}');
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
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:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
|
@ -75,8 +76,14 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
|||
return ethereum!.createEthereumRestoreWalletFromSeedCredentials(
|
||||
name: name, mnemonic: seed, password: password);
|
||||
case WalletType.nano:
|
||||
// default to bip39 for now:
|
||||
final DerivationType derivationType = DerivationType.bip39;
|
||||
return nano!.createNanoRestoreWalletFromSeedCredentials(
|
||||
name: name, mnemonic: seed, password: password);
|
||||
name: name,
|
||||
mnemonic: seed,
|
||||
password: password,
|
||||
derivationType: derivationType,
|
||||
);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -609,6 +609,7 @@ abstract class Nano {
|
|||
required String name,
|
||||
required String mnemonic,
|
||||
required String password,
|
||||
required DerivationType derivationType,
|
||||
});
|
||||
|
||||
String getTransactionAddress(Object wallet, int accountIndex, int addressIndex);
|
||||
|
|
Loading…
Reference in a new issue