seed generation

This commit is contained in:
fosse 2023-07-26 13:15:22 -04:00
parent 7f93f816de
commit 2670eb2868
14 changed files with 202 additions and 55 deletions

View file

@ -0,0 +1,4 @@
-
uri: rpc.nano.to
-
uri: eth.llamarpc.com

39
cw_nano/lib/file.dart Normal file
View 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);
}

View file

@ -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) {

View file

@ -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) {
@ -114,4 +120,4 @@ class NanoUtil {
// Ensure seed only contains hex characters, 0-9;A-F
return NanoHelpers.isHexString(seed);
}
}
}

View file

@ -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();
}

View file

@ -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;
}
}

View file

@ -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 = '';

View file

@ -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);

View file

@ -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

View file

@ -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);

View file

@ -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}');
}

View file

@ -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()}');
}

View file

@ -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;
}

View file

@ -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);