Skip to content

Commit 7e26d45

Browse files
committed
feat: zano hf + bip39
1 parent 787d454 commit 7e26d45

15 files changed

Lines changed: 157 additions & 23 deletions

cw_monero/pubspec.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -545,8 +545,8 @@ packages:
545545
dependency: "direct main"
546546
description:
547547
path: "impls/monero.dart"
548-
ref: "3bfb3856a838f2bf6b729501837bb0295dedf25d"
549-
resolved-ref: "3bfb3856a838f2bf6b729501837bb0295dedf25d"
548+
ref: "4f23e050f7dec0bbbbe27c36c8e511bb3f52868a"
549+
resolved-ref: "4f23e050f7dec0bbbbe27c36c8e511bb3f52868a"
550550
url: "https://github.com/mrcyjanek/monero_c.git"
551551
source: git
552552
version: "0.0.0"

cw_monero/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ dependencies:
2626
monero:
2727
git:
2828
url: https://github.com/mrcyjanek/monero_c.git
29-
ref: 3bfb3856a838f2bf6b729501837bb0295dedf25d
29+
ref: 4f23e050f7dec0bbbbe27c36c8e511bb3f52868a
3030
path: impls/monero.dart
3131
mutex: ^3.1.0
3232
ledger_flutter_plus: ^1.4.1

cw_wownero/pubspec.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -449,8 +449,8 @@ packages:
449449
dependency: "direct main"
450450
description:
451451
path: "impls/monero.dart"
452-
ref: "3bfb3856a838f2bf6b729501837bb0295dedf25d"
453-
resolved-ref: "3bfb3856a838f2bf6b729501837bb0295dedf25d"
452+
ref: "4f23e050f7dec0bbbbe27c36c8e511bb3f52868a"
453+
resolved-ref: "4f23e050f7dec0bbbbe27c36c8e511bb3f52868a"
454454
url: "https://github.com/mrcyjanek/monero_c.git"
455455
source: git
456456
version: "0.0.0"

cw_wownero/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ dependencies:
2424
monero:
2525
git:
2626
url: https://github.com/mrcyjanek/monero_c.git
27-
ref: 3bfb3856a838f2bf6b729501837bb0295dedf25d
27+
ref: 4f23e050f7dec0bbbbe27c36c8e511bb3f52868a
2828
path: impls/monero.dart
2929
mutex: ^3.1.0
3030

cw_zano/lib/bip39_seed.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import 'dart:typed_data';
2+
3+
import 'package:bip39/bip39.dart' as bip39;
4+
5+
bool isBip39Seed(String mnemonic) => bip39.validateMnemonic(mnemonic);
6+
7+
String generateBip39Mnemonic({int strength = 128}) =>
8+
bip39.generateMnemonic(strength: strength);
9+
10+
String getSecretDerivationFromBip39(String mnemonic, {String passphrase = ''}) {
11+
final seed = bip39.mnemonicToSeed(mnemonic, passphrase: passphrase);
12+
return Uint8List.fromList(seed.sublist(0, 32)).toHexString();
13+
}
14+
15+
extension on Uint8List {
16+
String toHexString() =>
17+
map((b) => b.toRadixString(16).padLeft(2, '0')).join();
18+
}

cw_zano/lib/zano_wallet.dart

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import 'package:cw_zano/zano_wallet_api.dart';
3333
import 'package:cw_zano/zano_wallet_exceptions.dart';
3434
import 'package:cw_zano/zano_wallet_service.dart';
3535
import 'package:cw_zano/api/model/balance.dart';
36+
import 'package:cw_zano/bip39_seed.dart';
3637

3738
import 'package:mobx/mobx.dart';
3839

@@ -132,14 +133,25 @@ abstract class ZanoWalletBase
132133
final wallet = ZanoWallet(credentials.walletInfo!, await credentials.walletInfo!.getDerivationInfo(), credentials.password!);
133134
await wallet.initWallet();
134135
final path = await pathForWallet(name: credentials.name, type: credentials.walletInfo!.type);
135-
final createWalletResult = await wallet.createWallet(path, credentials.password!);
136+
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
137+
final mnemonic = generateBip39Mnemonic(strength: strength);
138+
final passphrase = credentials.passphrase ?? '';
139+
final secretDerivation = getSecretDerivationFromBip39(mnemonic, passphrase: passphrase);
140+
final creationTimestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000;
141+
final createWalletResult = await wallet.restoreWalletFromDerivations(
142+
path,
143+
credentials.password!,
144+
secretDerivation,
145+
creationTimestamp: creationTimestamp,
146+
);
136147
await wallet.initWallet();
137-
await wallet.parseCreateWalletResult(createWalletResult);
148+
await wallet.setBip39Secrets(mnemonic: mnemonic, creationTimestamp: creationTimestamp);
138149
if (credentials.passphrase != null) {
139150
await wallet.setPassphrase(credentials.passphrase!);
140-
wallet.seed = await createWalletResult.seed(wallet);
141-
wallet.passphrase = await wallet.getPassphrase();
142151
}
152+
await wallet.parseCreateWalletResult(createWalletResult);
153+
wallet.seed = mnemonic;
154+
wallet.passphrase = await wallet.getPassphrase();
143155
await wallet.init(createWalletResult.wi.address);
144156
return wallet;
145157
}
@@ -149,15 +161,33 @@ abstract class ZanoWalletBase
149161
final wallet = ZanoWallet(credentials.walletInfo!, await credentials.walletInfo!.getDerivationInfo(), credentials.password!);
150162
await wallet.initWallet();
151163
final path = await pathForWallet(name: credentials.name, type: credentials.walletInfo!.type);
152-
final createWalletResult = await wallet.restoreWalletFromSeed(
153-
path, credentials.password!, credentials.mnemonic, credentials.passphrase);
164+
final passphrase = credentials.passphrase ?? '';
165+
final CreateWalletResult createWalletResult;
166+
if (isBip39Seed(credentials.mnemonic)) {
167+
final secretDerivation =
168+
getSecretDerivationFromBip39(credentials.mnemonic, passphrase: passphrase);
169+
final creationTimestamp = (credentials.height ?? 0) > 0 ? credentials.height! : 0;
170+
createWalletResult = await wallet.restoreWalletFromDerivations(
171+
path,
172+
credentials.password!,
173+
secretDerivation,
174+
creationTimestamp: creationTimestamp,
175+
);
176+
await wallet.setBip39Secrets(
177+
mnemonic: credentials.mnemonic,
178+
creationTimestamp: creationTimestamp,
179+
);
180+
} else {
181+
createWalletResult = await wallet.restoreWalletFromSeed(
182+
path, credentials.password!, credentials.mnemonic, credentials.passphrase);
183+
}
154184
await wallet.initWallet();
155-
await wallet.parseCreateWalletResult(createWalletResult);
156185
if (credentials.passphrase != null) {
157186
await wallet.setPassphrase(credentials.passphrase!);
158-
wallet.seed = await createWalletResult.seed(wallet);
159-
wallet.passphrase = await wallet.getPassphrase();
160187
}
188+
await wallet.parseCreateWalletResult(createWalletResult);
189+
wallet.seed = await createWalletResult.seed(wallet);
190+
wallet.passphrase = await wallet.getPassphrase();
161191
await wallet.init(createWalletResult.wi.address);
162192
return wallet;
163193
}

cw_zano/lib/zano_wallet_api.dart

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,25 @@ mixin ZanoWalletApi {
123123
return _setWalletSecret("passphrase", passphrase);
124124
}
125125

126+
Future<String?> getBip39Mnemonic() async {
127+
return await _getWalletSecret("bip39_mnemonic");
128+
}
129+
130+
Future<void> setBip39Secrets({
131+
required String mnemonic,
132+
required int creationTimestamp,
133+
}) async {
134+
final secrets = await _getSecrets();
135+
secrets["bip39_mnemonic"] = mnemonic;
136+
secrets["creation_timestamp"] = creationTimestamp.toString();
137+
await _setSecrets(secrets);
138+
}
139+
126140
Future<String> getSeed() async {
141+
final bip39Mnemonic = await getBip39Mnemonic();
142+
if (bip39Mnemonic != null) {
143+
return bip39Mnemonic;
144+
}
127145
final passphrase = await getPassphrase();
128146
final respStr = await invokeMethod("get_restore_info", {"seed_password": passphrase??""});
129147
final resp = convert.json.decode(respStr);
@@ -319,6 +337,43 @@ mixin ZanoWalletApi {
319337
return result;
320338
}
321339

340+
Future<CreateWalletResult> restoreWalletFromDerivations(
341+
String path,
342+
String password,
343+
String secretDerivation, {
344+
bool isAuditable = false,
345+
required int creationTimestamp,
346+
}) async {
347+
printV('restore_wallet_from_derivations path $path');
348+
final params = jsonEncode({
349+
"creation_timestamp": creationTimestamp,
350+
"is_auditable": isAuditable,
351+
"pass": password,
352+
"path": path,
353+
"secret_derivation": secretDerivation,
354+
});
355+
final json = await callSyncMethod('restore_from_derivations', 0, params);
356+
final map = jsonDecode(json) as Map<String, dynamic>?;
357+
if (map?['error'] != null) {
358+
final code = map!['error']!['code'] ?? '';
359+
final message = map['error']!['message'] ?? '';
360+
if (code == Consts.errorWrongSeed) {
361+
throw RestoreFromSeedsException(
362+
'Error restoring wallet\nPlease check the seed words are correct. Additionally, if you created this wallet with a passphrase please add it under the Advanced Settings page.');
363+
} else if (code == Consts.errorAlreadyExists) {
364+
throw RestoreFromSeedsException('Error restoring wallet, already exists');
365+
}
366+
throw RestoreFromSeedsException('Error restoring wallet, $message ($code)');
367+
}
368+
if (map?['result'] == null) {
369+
throw RestoreFromSeedsException('Error restoring wallet, empty response');
370+
}
371+
final result = CreateWalletResult.fromJson(map!['result'] as Map<String, dynamic>);
372+
openWalletCache[path] = result;
373+
printV('restore_wallet_from_derivations ${result.name} ${result.wi.address}');
374+
return result;
375+
}
376+
322377
Future<CreateWalletResult> restoreWalletFromSeed(String path, String password, String seed, String? passphrase) async {
323378
printV('restore_wallet path $path');
324379
final json = zano.PlainWallet_restore(seed, path, password, passphrase??'');

cw_zano/pubspec.lock

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ packages:
5050
url: "https://github.com/cake-tech/bech32.git"
5151
source: git
5252
version: "0.2.2"
53+
bip39:
54+
dependency: "direct main"
55+
description:
56+
name: bip39
57+
sha256: de1ee27ebe7d96b84bb3a04a4132a0a3007dcdd5ad27dd14aa87a29d97c45edc
58+
url: "https://pub.dev"
59+
source: hosted
60+
version: "1.0.6"
5361
blockchain_utils:
5462
dependency: transitive
5563
description:
@@ -298,6 +306,14 @@ packages:
298306
url: "https://pub.dev"
299307
source: hosted
300308
version: "2.3.2"
309+
hex:
310+
dependency: transitive
311+
description:
312+
name: hex
313+
sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a"
314+
url: "https://pub.dev"
315+
source: hosted
316+
version: "0.2.0"
301317
hive:
302318
dependency: "direct dev"
303319
description:
@@ -454,8 +470,8 @@ packages:
454470
dependency: "direct main"
455471
description:
456472
path: "impls/monero.dart"
457-
ref: "3bfb3856a838f2bf6b729501837bb0295dedf25d"
458-
resolved-ref: "3bfb3856a838f2bf6b729501837bb0295dedf25d"
473+
ref: "4f23e050f7dec0bbbbe27c36c8e511bb3f52868a"
474+
resolved-ref: "4f23e050f7dec0bbbbe27c36c8e511bb3f52868a"
459475
url: "https://github.com/mrcyjanek/monero_c.git"
460476
source: git
461477
version: "0.0.0"

cw_zano/pubspec.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ environment:
1111
dependencies:
1212
flutter:
1313
sdk: flutter
14+
bip39: ^1.0.6
1415
ffi: ^2.0.1
1516
http: ^1.1.0
1617
path_provider: ^2.0.11
@@ -25,7 +26,7 @@ dependencies:
2526
monero:
2627
git:
2728
url: https://github.com/mrcyjanek/monero_c.git
28-
ref: 3bfb3856a838f2bf6b729501837bb0295dedf25d
29+
ref: 4f23e050f7dec0bbbbe27c36c8e511bb3f52868a
2930
path: impls/monero.dart
3031
dev_dependencies:
3132
flutter_test:

lib/core/wallet_creation_service.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,13 @@ class WalletCreationService {
8686
case WalletType.nano:
8787
case WalletType.zcash:
8888
return true;
89+
case WalletType.zano:
90+
return true;
8991
case WalletType.monero:
9092
case WalletType.wownero:
9193
case WalletType.none:
9294
case WalletType.haven:
9395
case WalletType.banano:
94-
case WalletType.zano:
9596
case WalletType.decred:
9697
return false;
9798
}

0 commit comments

Comments
 (0)