mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-04-15 00:41:54 +00:00
CW-934 Implement passphrase creation for zano (#2026)
* CW-934 Implement passphrase creation for zano * Update monero_c dependency to latest commit Fix issue with zano keys not showing during sync Fix delays when invoking read only commands in zano Fix extra padding above passphrase Reduced lag during app use
This commit is contained in:
parent
9d6f985a59
commit
dd8ccee1ba
18 changed files with 143 additions and 41 deletions
cw_monero
cw_wownero
cw_zano
lib
src/screens
view_model
zano
scripts
tool
|
@ -511,8 +511,8 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
path: "impls/monero.dart"
|
||||
ref: "629fa4a346ca29d5ed18a2b44895b8858ba7c9f7"
|
||||
resolved-ref: "629fa4a346ca29d5ed18a2b44895b8858ba7c9f7"
|
||||
ref: "65608c09e9093f1cd42c6afd8e9131016c82574b"
|
||||
resolved-ref: "65608c09e9093f1cd42c6afd8e9131016c82574b"
|
||||
url: "https://github.com/mrcyjanek/monero_c"
|
||||
source: git
|
||||
version: "0.0.0"
|
||||
|
|
|
@ -25,7 +25,7 @@ dependencies:
|
|||
monero:
|
||||
git:
|
||||
url: https://github.com/mrcyjanek/monero_c
|
||||
ref: 629fa4a346ca29d5ed18a2b44895b8858ba7c9f7
|
||||
ref: 65608c09e9093f1cd42c6afd8e9131016c82574b
|
||||
path: impls/monero.dart
|
||||
mutex: ^3.1.0
|
||||
ledger_flutter_plus: ^1.4.1
|
||||
|
|
|
@ -471,8 +471,8 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
path: "impls/monero.dart"
|
||||
ref: "629fa4a346ca29d5ed18a2b44895b8858ba7c9f7"
|
||||
resolved-ref: "629fa4a346ca29d5ed18a2b44895b8858ba7c9f7"
|
||||
ref: "65608c09e9093f1cd42c6afd8e9131016c82574b"
|
||||
resolved-ref: "65608c09e9093f1cd42c6afd8e9131016c82574b"
|
||||
url: "https://github.com/mrcyjanek/monero_c"
|
||||
source: git
|
||||
version: "0.0.0"
|
||||
|
|
|
@ -25,7 +25,7 @@ dependencies:
|
|||
monero:
|
||||
git:
|
||||
url: https://github.com/mrcyjanek/monero_c
|
||||
ref: 629fa4a346ca29d5ed18a2b44895b8858ba7c9f7 # monero_c hash
|
||||
ref: 65608c09e9093f1cd42c6afd8e9131016c82574b # monero_c hash
|
||||
path: impls/monero.dart
|
||||
mutex: ^3.1.0
|
||||
|
||||
|
|
|
@ -1,27 +1,34 @@
|
|||
import 'package:cw_zano/api/model/recent_history.dart';
|
||||
import 'package:cw_zano/api/model/wi.dart';
|
||||
import 'package:cw_zano/zano_wallet.dart';
|
||||
|
||||
class CreateWalletResult {
|
||||
final String name;
|
||||
final String pass;
|
||||
final RecentHistory recentHistory;
|
||||
final bool recovered;
|
||||
final String seed;
|
||||
final int walletFileSize;
|
||||
final int walletId;
|
||||
final int walletLocalBcSize;
|
||||
final Wi wi;
|
||||
final String privateSpendKey;
|
||||
final String privateViewKey;
|
||||
final String publicSpendKey;
|
||||
final String publicViewKey;
|
||||
|
||||
CreateWalletResult(
|
||||
{required this.name,
|
||||
required this.pass,
|
||||
required this.recentHistory,
|
||||
required this.recovered,
|
||||
required this.seed,
|
||||
required this.walletFileSize,
|
||||
required this.walletId,
|
||||
required this.walletLocalBcSize,
|
||||
required this.wi});
|
||||
required this.wi,
|
||||
required this.privateSpendKey,
|
||||
required this.privateViewKey,
|
||||
required this.publicSpendKey,
|
||||
required this.publicViewKey});
|
||||
|
||||
factory CreateWalletResult.fromJson(Map<String, dynamic> json) =>
|
||||
CreateWalletResult(
|
||||
|
@ -30,10 +37,16 @@ class CreateWalletResult {
|
|||
recentHistory: RecentHistory.fromJson(
|
||||
json['recent_history'] as Map<String, dynamic>? ?? {}),
|
||||
recovered: json['recovered'] as bool? ?? false,
|
||||
seed: json['seed'] as String? ?? '',
|
||||
walletFileSize: json['wallet_file_size'] as int? ?? 0,
|
||||
walletId: json['wallet_id'] as int? ?? 0,
|
||||
walletLocalBcSize: json['wallet_local_bc_size'] as int? ?? 0,
|
||||
wi: Wi.fromJson(json['wi'] as Map<String, dynamic>? ?? {}),
|
||||
privateSpendKey: json['private_spend_key'] as String? ?? '',
|
||||
privateViewKey: json['private_view_key'] as String? ?? '',
|
||||
publicSpendKey: json['public_spend_key'] as String? ?? '',
|
||||
publicViewKey: json['public_view_key'] as String? ?? '',
|
||||
);
|
||||
Future<String> seed(ZanoWalletBase api) {
|
||||
return api.getSeed();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
import 'package:cw_zano/zano_wallet.dart';
|
||||
|
||||
class WiExtended {
|
||||
final String seed;
|
||||
final String spendPrivateKey;
|
||||
final String spendPublicKey;
|
||||
final String viewPrivateKey;
|
||||
final String viewPublicKey;
|
||||
|
||||
WiExtended({required this.seed, required this.spendPrivateKey, required this.spendPublicKey, required this.viewPrivateKey, required this.viewPublicKey});
|
||||
WiExtended({required this.spendPrivateKey, required this.spendPublicKey, required this.viewPrivateKey, required this.viewPublicKey});
|
||||
|
||||
factory WiExtended.fromJson(Map<String, dynamic> json) => WiExtended(
|
||||
seed: json['seed'] as String? ?? '',
|
||||
spendPrivateKey: json['spend_private_key'] as String? ?? '',
|
||||
spendPublicKey: json['spend_public_key'] as String? ?? '',
|
||||
viewPrivateKey: json['view_private_key'] as String? ?? '',
|
||||
viewPublicKey: json['view_public_key'] as String? ?? '',
|
||||
);
|
||||
|
||||
Future<String> seed(ZanoWalletBase api) {
|
||||
return api.getSeed();
|
||||
}
|
||||
}
|
|
@ -82,6 +82,9 @@ abstract class ZanoWalletBase
|
|||
@override
|
||||
String seed = '';
|
||||
|
||||
@override
|
||||
String? passphrase = '';
|
||||
|
||||
@override
|
||||
ZanoWalletKeys keys = ZanoWalletKeys(
|
||||
privateSpendKey: '', privateViewKey: '', publicSpendKey: '', publicViewKey: '');
|
||||
|
@ -133,6 +136,11 @@ abstract class ZanoWalletBase
|
|||
final createWalletResult = await wallet.createWallet(path, credentials.password!);
|
||||
await wallet.initWallet();
|
||||
await wallet.parseCreateWalletResult(createWalletResult);
|
||||
if (credentials.passphrase != null) {
|
||||
await wallet.setPassphrase(credentials.passphrase!);
|
||||
wallet.seed = await createWalletResult.seed(wallet);
|
||||
wallet.passphrase = await wallet.getPassphrase();
|
||||
}
|
||||
await wallet.init(createWalletResult.wi.address);
|
||||
return wallet;
|
||||
}
|
||||
|
@ -146,6 +154,11 @@ abstract class ZanoWalletBase
|
|||
path, credentials.password!, credentials.mnemonic, credentials.passphrase);
|
||||
await wallet.initWallet();
|
||||
await wallet.parseCreateWalletResult(createWalletResult);
|
||||
if (credentials.passphrase != null) {
|
||||
await wallet.setPassphrase(credentials.passphrase!);
|
||||
wallet.seed = await createWalletResult.seed(wallet);
|
||||
wallet.passphrase = await wallet.getPassphrase();
|
||||
}
|
||||
await wallet.init(createWalletResult.wi.address);
|
||||
return wallet;
|
||||
}
|
||||
|
@ -172,7 +185,15 @@ abstract class ZanoWalletBase
|
|||
|
||||
Future<void> parseCreateWalletResult(CreateWalletResult result) async {
|
||||
hWallet = result.walletId;
|
||||
seed = result.seed;
|
||||
seed = await result.seed(this);
|
||||
keys = ZanoWalletKeys(
|
||||
privateSpendKey: result.privateSpendKey,
|
||||
privateViewKey: result.privateViewKey,
|
||||
publicSpendKey: result.publicSpendKey,
|
||||
publicViewKey: result.publicViewKey,
|
||||
);
|
||||
passphrase = await getPassphrase();
|
||||
|
||||
printV('setting hWallet = ${result.walletId}');
|
||||
walletAddresses.address = result.wi.address;
|
||||
await loadAssets(result.wi.balances, maxRetries: _maxLoadAssetsRetries);
|
||||
|
@ -511,7 +532,7 @@ abstract class ZanoWalletBase
|
|||
// we can call getWalletInfo ONLY if getWalletStatus returns NOT is in long refresh and wallet state is 2 (ready)
|
||||
if (!walletStatus.isInLongRefresh && walletStatus.walletState == 2) {
|
||||
final walletInfo = await getWalletInfo();
|
||||
seed = walletInfo.wiExtended.seed;
|
||||
seed = await walletInfo.wiExtended.seed(this);
|
||||
keys = ZanoWalletKeys(
|
||||
privateSpendKey: walletInfo.wiExtended.spendPrivateKey,
|
||||
privateViewKey: walletInfo.wiExtended.viewPrivateKey,
|
||||
|
|
|
@ -26,6 +26,7 @@ import 'package:ffi/ffi.dart';
|
|||
import 'package:json_bigint/json_bigint.dart';
|
||||
import 'package:monero/zano.dart' as zano;
|
||||
import 'package:monero/src/generated_bindings_zano.g.dart' as zanoapi;
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
mixin ZanoWalletApi {
|
||||
static const _maxReopenAttempts = 5;
|
||||
|
@ -45,7 +46,7 @@ mixin ZanoWalletApi {
|
|||
void setPassword(String password) => zano.PlainWallet_resetWalletPassword(hWallet, password);
|
||||
|
||||
void closeWallet(int? walletToClose, {bool force = false}) async {
|
||||
printV('close_wallet ${walletToClose ?? hWallet}');
|
||||
printV('close_wallet ${walletToClose ?? hWallet}: $force');
|
||||
if (Platform.isWindows || force) {
|
||||
final result = await _closeWallet(walletToClose ?? hWallet);
|
||||
printV('close_wallet result $result');
|
||||
|
@ -53,10 +54,9 @@ mixin ZanoWalletApi {
|
|||
}
|
||||
}
|
||||
|
||||
bool isInit = false;
|
||||
static bool isInit = false;
|
||||
|
||||
Future<bool> initWallet() async {
|
||||
// pathForWallet(name: , type: type)
|
||||
if (isInit) return true;
|
||||
final result = zano.PlainWallet_init("", "", 0);
|
||||
isInit = true;
|
||||
|
@ -68,6 +68,68 @@ mixin ZanoWalletApi {
|
|||
return true;
|
||||
}
|
||||
|
||||
Future<Directory> getWalletDir() async {
|
||||
final walletInfoResult = await getWalletInfo();
|
||||
return Directory(p.dirname(walletInfoResult.wi.path));
|
||||
}
|
||||
|
||||
Future<File> _getWalletSecretsFile() async {
|
||||
final dir = await getWalletDir();
|
||||
final file = File(p.join(dir.path, "zano-secrets.json.bin"));
|
||||
return file;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> _getSecrets() async {
|
||||
final file = await _getWalletSecretsFile();
|
||||
if (!file.existsSync()) {
|
||||
return {};
|
||||
}
|
||||
final data = file.readAsBytesSync();
|
||||
final b64 = convert.base64.encode(data);
|
||||
final respStr = await invokeMethod("decrypt_data", {"buff": "$b64"});
|
||||
final resp = convert.json.decode(respStr);
|
||||
final dataBytes = convert.base64.decode(resp["result"]["res_buff"] as String);
|
||||
final dataStr = convert.utf8.decode(dataBytes);
|
||||
final dataObject = convert.json.decode(dataStr);
|
||||
return dataObject as Map<String, dynamic>;
|
||||
}
|
||||
|
||||
Future<void> _setSecrets(Map<String, dynamic> data) async {
|
||||
final dataStr = convert.json.encode(data);
|
||||
final b64 = convert.base64.encode(convert.utf8.encode(dataStr));
|
||||
final respStr = await invokeMethod("encrypt_data", {"buff": "$b64"});
|
||||
final resp = convert.json.decode(respStr);
|
||||
final dataBytes = convert.base64.decode(resp["result"]["res_buff"] as String);
|
||||
final file = await _getWalletSecretsFile();
|
||||
file.writeAsBytesSync(dataBytes);
|
||||
}
|
||||
|
||||
Future<String?> _getWalletSecret(String key) async {
|
||||
final secrets = await _getSecrets();
|
||||
return secrets[key] as String?;
|
||||
}
|
||||
|
||||
Future<void> _setWalletSecret(String key, String value) async {
|
||||
final secrets = await _getSecrets();
|
||||
secrets[key] = value;
|
||||
await _setSecrets(secrets);
|
||||
}
|
||||
|
||||
Future<String?> getPassphrase() async {
|
||||
return await _getWalletSecret("passphrase");
|
||||
}
|
||||
|
||||
Future<void> setPassphrase(String passphrase) {
|
||||
return _setWalletSecret("passphrase", passphrase);
|
||||
}
|
||||
|
||||
Future<String> getSeed() async {
|
||||
final passphrase = await getPassphrase();
|
||||
final respStr = await invokeMethod("get_restore_info", {"seed_password": passphrase??""});
|
||||
final resp = convert.json.decode(respStr);
|
||||
return resp["result"]["seed_phrase"] as String;
|
||||
}
|
||||
|
||||
Future<GetWalletInfoResult> getWalletInfo() async {
|
||||
final json = await _getWalletInfo(hWallet);
|
||||
final result = GetWalletInfoResult.fromJson(jsonDecode(json));
|
||||
|
@ -192,7 +254,7 @@ mixin ZanoWalletApi {
|
|||
|
||||
Future<StoreResult?> store() async {
|
||||
try {
|
||||
final json = await invokeMethod('store', '{}');
|
||||
final json = await invokeMethod('store', {});
|
||||
final map = jsonDecode(json) as Map<String, dynamic>?;
|
||||
_checkForErrors(map);
|
||||
return StoreResult.fromJson(map!['result'] as Map<String, dynamic>);
|
||||
|
@ -247,12 +309,12 @@ mixin ZanoWalletApi {
|
|||
}
|
||||
final result = CreateWalletResult.fromJson(map!['result'] as Map<String, dynamic>);
|
||||
openWalletCache[path] = result;
|
||||
printV('create_wallet ${result.name} ${result.seed}');
|
||||
printV('create_wallet ${result.name}');
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<CreateWalletResult> restoreWalletFromSeed(String path, String password, String seed, String? passphrase) async {
|
||||
printV('restore_wallet path $path password ${_shorten(password)} seed ${_shorten(seed)}');
|
||||
printV('restore_wallet path $path');
|
||||
final json = zano.PlainWallet_restore(seed, path, password, passphrase??'');
|
||||
final map = jsonDecode(json) as Map<String, dynamic>?;
|
||||
if (map?['error'] != null) {
|
||||
|
@ -274,8 +336,8 @@ mixin ZanoWalletApi {
|
|||
return result;
|
||||
}
|
||||
|
||||
Future<CreateWalletResult>loadWallet(String path, String password, [int attempt = 0]) async {
|
||||
printV('load_wallet1 path $path password ${_shorten(password)}');
|
||||
Future<CreateWalletResult> loadWallet(String path, String password, [int attempt = 0]) async {
|
||||
printV('load_wallet1 path $path');
|
||||
final String json;
|
||||
try {
|
||||
json = zano.PlainWallet_open(path, password);
|
||||
|
@ -283,7 +345,7 @@ mixin ZanoWalletApi {
|
|||
printV('error in loadingWallet $e');
|
||||
rethrow;
|
||||
}
|
||||
// printV('load_wallet2: $json');
|
||||
|
||||
final map = jsonDecode(json) as Map<String, dynamic>?;
|
||||
if (map?['error'] != null) {
|
||||
final code = map?['error']!['code'] ?? '';
|
||||
|
@ -435,10 +497,8 @@ Future<String> _getWalletInfo(int hWallet) async {
|
|||
}
|
||||
|
||||
Future<String> _setupNode(int hWallet, String nodeUrl) async {
|
||||
final resp = await callSyncMethod("reset_connection_url", hWallet, nodeUrl);
|
||||
printV(resp);
|
||||
final resp2 = await callSyncMethod("run_wallet", hWallet, "");
|
||||
printV(resp2);
|
||||
await callSyncMethod("reset_connection_url", hWallet, nodeUrl);
|
||||
await callSyncMethod("run_wallet", hWallet, "");
|
||||
return "OK";
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import 'package:hive/hive.dart';
|
|||
import 'package:monero/zano.dart' as zano;
|
||||
|
||||
class ZanoNewWalletCredentials extends WalletCredentials {
|
||||
ZanoNewWalletCredentials({required String name, String? password}) : super(name: name, password: password);
|
||||
ZanoNewWalletCredentials({required String name, String? password, required String? passphrase}) : super(name: name, password: password, passphrase: passphrase);
|
||||
}
|
||||
|
||||
class ZanoRestoreWalletFromSeedCredentials extends WalletCredentials {
|
||||
|
|
|
@ -476,8 +476,8 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
path: "impls/monero.dart"
|
||||
ref: "629fa4a346ca29d5ed18a2b44895b8858ba7c9f7"
|
||||
resolved-ref: "629fa4a346ca29d5ed18a2b44895b8858ba7c9f7"
|
||||
ref: "65608c09e9093f1cd42c6afd8e9131016c82574b"
|
||||
resolved-ref: "65608c09e9093f1cd42c6afd8e9131016c82574b"
|
||||
url: "https://github.com/mrcyjanek/monero_c"
|
||||
source: git
|
||||
version: "0.0.0"
|
||||
|
|
|
@ -26,7 +26,7 @@ dependencies:
|
|||
monero:
|
||||
git:
|
||||
url: https://github.com/mrcyjanek/monero_c
|
||||
ref: 629fa4a346ca29d5ed18a2b44895b8858ba7c9f7 # monero_c hash
|
||||
ref: 65608c09e9093f1cd42c6afd8e9131016c82574b # monero_c hash
|
||||
path: impls/monero.dart
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
|
@ -202,7 +202,7 @@ class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBo
|
|||
);
|
||||
return Container();
|
||||
}),
|
||||
if (widget.privacySettingsViewModel.hasPassphraseOption(widget.isFromRestore))
|
||||
if (widget.privacySettingsViewModel.hasPassphraseOption)
|
||||
Padding(
|
||||
padding: EdgeInsets.all(24),
|
||||
child: Form(
|
||||
|
|
|
@ -160,8 +160,11 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
|
|||
Widget _buildSeedTab(BuildContext context, bool isLegacySeed) {
|
||||
return Column(
|
||||
children: [
|
||||
if (isLegacySeedOnly || isLegacySeed) _buildHeightBox(),
|
||||
const SizedBox(height: 20),
|
||||
if (isLegacySeedOnly || isLegacySeed)
|
||||
...[
|
||||
_buildHeightBox(),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
(_buildPassphraseBox() ?? Container()),
|
||||
if (widget.walletKeysViewModel.passphrase.isNotEmpty) const SizedBox(height: 20),
|
||||
Expanded(
|
||||
|
|
|
@ -71,7 +71,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
|
|||
|
||||
bool get isNanoSeedTypeOptionsEnabled => [WalletType.nano].contains(type);
|
||||
|
||||
bool hasPassphraseOption(bool isRestore) => [
|
||||
bool get hasPassphraseOption => [
|
||||
WalletType.bitcoin,
|
||||
WalletType.litecoin,
|
||||
WalletType.bitcoinCash,
|
||||
|
@ -80,7 +80,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
|
|||
WalletType.tron,
|
||||
WalletType.monero,
|
||||
WalletType.wownero,
|
||||
if (isRestore) WalletType.zano,
|
||||
WalletType.zano,
|
||||
].contains(type);
|
||||
|
||||
@computed
|
||||
|
|
|
@ -175,6 +175,7 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
|
|||
return zano!.createZanoNewWalletCredentials(
|
||||
name: name,
|
||||
password: walletPassword,
|
||||
passphrase: passphrase,
|
||||
);
|
||||
case WalletType.none:
|
||||
throw Exception('Unexpected type: ${type.toString()}');
|
||||
|
|
|
@ -53,8 +53,8 @@ class CWZano extends Zano {
|
|||
}
|
||||
|
||||
@override
|
||||
WalletCredentials createZanoNewWalletCredentials({required String name, required String? password}) {
|
||||
return ZanoNewWalletCredentials(name: name, password: password);
|
||||
WalletCredentials createZanoNewWalletCredentials({required String name, required String? password, required String? passphrase}) {
|
||||
return ZanoNewWalletCredentials(name: name, password: password, passphrase: passphrase);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -8,7 +8,7 @@ if [[ ! -d "monero_c/.git" ]];
|
|||
then
|
||||
git clone https://github.com/mrcyjanek/monero_c --branch master monero_c
|
||||
cd monero_c
|
||||
git checkout 629fa4a346ca29d5ed18a2b44895b8858ba7c9f7
|
||||
git checkout 65608c09e9093f1cd42c6afd8e9131016c82574b
|
||||
git reset --hard
|
||||
git submodule update --init --force --recursive
|
||||
./apply_patches.sh monero
|
||||
|
|
|
@ -1443,7 +1443,7 @@ abstract class Zano {
|
|||
List<String> getWordList(String language);
|
||||
|
||||
WalletCredentials createZanoRestoreWalletFromSeedCredentials({required String name, required String password, required String passphrase, required int height, required String mnemonic});
|
||||
WalletCredentials createZanoNewWalletCredentials({required String name, required String? password});
|
||||
WalletCredentials createZanoNewWalletCredentials({required String name, required String? password, required String? passphrase});
|
||||
Map<String, String> getKeys(Object wallet);
|
||||
Object createZanoTransactionCredentials({required List<Output> outputs, required TransactionPriority priority, required CryptoCurrency currency});
|
||||
double formatterIntAmountToDouble({required int amount, required CryptoCurrency currency, required bool forFee});
|
||||
|
|
Loading…
Reference in a new issue