CW-723-Add-Monero-support-to-the-Shared-Seed-feature-in-Cake (#2131)

* feat: add exodus style bip39 to monero legacy seed

* feat: restore monero wallet from bip39 and add test

* bug: fix wrong naming in CI

* feat: add monero bip39 UI flow

* fix: monero.dart generation

* fix: skip monero_wallet_service tests till CI is fixed

* ci: copy monero_libwallet2_api_c.so to /usr/lib for testing
ci: reduce timeout for cw_monero tests

* fix: monero wallet creation credentials default to bip39 if mnemonic are set

* fix: do not skip monero wallets services test

* fix: Include non bip39 monero wallets on Wallet Group

* fix: null pointer stemming from missing language selector if seed is selected

* fix: Fixes to Bip39 Creation and restore

- Do not restore from 0 for fresh bip39 wallet
- disallow restoring bip39 wallet without date or height

* fix: Fixes to Bip39 restore

- Refresh height is now getting set correctly
- Add new create monero wallet tests
- Add seed-language English for Bip39 Monero wallets
- Fix seed-type naming

* feat (cw_monero): Store monero wallet after bip39 creation

* feat (cw_monero): remove prints from monero_wallet_service_test.dart

* fix: exception during seed language autodetect

* feat (cw_monero): Add support for passphrases on bip39 seeds

* feat (cw_monero): Add support for passphrases on bip39 seeds

* fix: seed language selection for recovering bip39 wallets

* style: improve readability of isLegacySeedOnly in wallet_keys_view_model.dart

* feat: hide monero seed type selector from advanced settings when creating a child wallet

* fix(cw_monero): use named arguments for bip39_seed tests

---------

Co-authored-by: cyan <cyjan@mrcyjanek.net>
This commit is contained in:
Konstantin Ullrich 2025-04-10 03:31:26 +02:00 committed by GitHub
parent 494207290e
commit f58a5fb8fd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
51 changed files with 702 additions and 283 deletions

View file

@ -11,4 +11,5 @@ android/.externalNativeBuild/
android/.cxx/
macos/cw_monero.podspec
macos/External/
macos/External/
*monero_libwallet2_api_c.*

View file

@ -62,6 +62,11 @@ String getSeed() {
}
return cakepolyseed;
}
final bip39 = monero.Wallet_getCacheAttribute(wptr!, key: "cakewallet.seed.bip39");
if(bip39.isNotEmpty) return bip39;
final legacy = getSeedLegacy(null);
return legacy;
}

View file

@ -0,0 +1,59 @@
import 'dart:typed_data';
import 'package:bip32/bip32.dart' as bip32;
import 'package:bip39/bip39.dart' as bip39;
import 'package:polyseed/polyseed.dart';
bool isBip39Seed(String mnemonic) => bip39.validateMnemonic(mnemonic);
String getBip39Seed() => bip39.generateMnemonic();
String getLegacySeedFromBip39(String mnemonic,
{int accountIndex = 0, String passphrase = ""}) {
final seed = bip39.mnemonicToSeed(mnemonic, passphrase: passphrase);
final bip32KeyPair =
bip32.BIP32.fromSeed(seed).derivePath("m/44'/128'/$accountIndex'/0/0");
final spendKey = _reduceECKey(bip32KeyPair.privateKey!);
return LegacySeedLang.getByEnglishName("English")
.encodePhrase(spendKey.toHexString());
}
const _ed25519CurveOrder =
"1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED";
Uint8List _reduceECKey(Uint8List buffer) {
final curveOrder = BigInt.parse(_ed25519CurveOrder, radix: 16);
final bigNumber = _readBytes(buffer);
var result = bigNumber % curveOrder;
final resultBuffer = Uint8List(32);
for (var i = 0; i < 32; i++) {
resultBuffer[i] = (result & BigInt.from(0xff)).toInt();
result = result >> 8;
}
return resultBuffer;
}
/// Read BigInt from a little-endian Uint8List
/// From https://github.com/dart-lang/sdk/issues/32803#issuecomment-387405784
BigInt _readBytes(Uint8List bytes) {
BigInt read(int start, int end) {
if (end - start <= 4) {
var result = 0;
for (int i = end - 1; i >= start; i--) {
result = result * 256 + bytes[i];
}
return BigInt.from(result);
}
final mid = start + ((end - start) >> 1);
return read(start, mid) +
read(mid, end) * (BigInt.one << ((mid - start) * 8));
}
return read(0, bytes.length);
}

View file

@ -1,6 +1,7 @@
import 'dart:ffi';
import 'dart:io';
import 'package:collection/collection.dart';
import 'package:cw_core/get_height_by_date.dart';
import 'package:cw_core/monero_wallet_utils.dart';
import 'package:cw_core/pathForWallet.dart';
@ -14,29 +15,38 @@ import 'package:cw_core/wallet_type.dart';
import 'package:cw_monero/api/account_list.dart';
import 'package:cw_monero/api/wallet_manager.dart' as monero_wallet_manager;
import 'package:cw_monero/api/wallet_manager.dart';
import 'package:cw_monero/bip39_seed.dart';
import 'package:cw_monero/ledger.dart';
import 'package:cw_monero/monero_wallet.dart';
import 'package:collection/collection.dart';
import 'package:hive/hive.dart';
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
import 'package:monero/monero.dart' as monero;
import 'package:polyseed/polyseed.dart';
enum MoneroSeedType { polyseed, legacy, bip39 }
class MoneroNewWalletCredentials extends WalletCredentials {
MoneroNewWalletCredentials(
{required String name, required this.language, required this.isPolyseed, String? password, this.passphrase})
{required String name,
required this.language,
required this.seedType,
String? password,
this.passphrase,
this.mnemonic})
: super(name: name, password: password);
final String language;
final bool isPolyseed;
final MoneroSeedType seedType;
final String? passphrase;
final String? mnemonic;
}
class MoneroRestoreWalletFromHardwareCredentials extends WalletCredentials {
MoneroRestoreWalletFromHardwareCredentials({required String name,
required this.ledgerConnection,
int height = 0,
String? password})
MoneroRestoreWalletFromHardwareCredentials(
{required String name,
required this.ledgerConnection,
int height = 0,
String? password})
: super(name: name, password: password, height: height);
LedgerConnection ledgerConnection;
}
@ -60,13 +70,14 @@ class MoneroWalletLoadingException implements Exception {
}
class MoneroRestoreWalletFromKeysCredentials extends WalletCredentials {
MoneroRestoreWalletFromKeysCredentials({required String name,
required String password,
required this.language,
required this.address,
required this.viewKey,
required this.spendKey,
int height = 0})
MoneroRestoreWalletFromKeysCredentials(
{required String name,
required String password,
required this.language,
required this.address,
required this.viewKey,
required this.spendKey,
int height = 0})
: super(name: name, password: password, height: height);
final String language;
@ -97,27 +108,42 @@ class MoneroWalletService extends WalletService<
@override
WalletType getType() => WalletType.monero;
@override
Future<MoneroWallet> create(MoneroNewWalletCredentials credentials, {bool? isTestnet}) async {
@override
Future<MoneroWallet> create(MoneroNewWalletCredentials credentials,
{bool? isTestnet}) async {
try {
final path = await pathForWallet(name: credentials.name, type: getType());
if (credentials.isPolyseed) {
if (credentials.seedType == MoneroSeedType.bip39) {
return _restoreFromBip39(
path: path,
password: credentials.password!,
mnemonic: credentials.mnemonic ?? getBip39Seed(),
passphrase: credentials.passphrase,
walletInfo: credentials.walletInfo!,
);
}
if (credentials.seedType == MoneroSeedType.polyseed) {
final polyseed = Polyseed.create();
final lang = PolyseedLang.getByEnglishName(credentials.language);
if (credentials.passphrase != null) polyseed.crypt(credentials.passphrase!);
if (credentials.passphrase != null)
polyseed.crypt(credentials.passphrase!);
final heightOverride =
getMoneroHeigthByDate(date: DateTime.now().subtract(Duration(days: 2)));
final heightOverride = getMoneroHeigthByDate(
date: DateTime.now().subtract(Duration(days: 2)));
return _restoreFromPolyseed(
path, credentials.password!, polyseed, credentials.walletInfo!, lang,
return _restoreFromPolyseed(path, credentials.password!, polyseed,
credentials.walletInfo!, lang,
overrideHeight: heightOverride, passphrase: credentials.passphrase);
}
await monero_wallet_manager.createWallet(
path: path, password: credentials.password!, language: credentials.language, passphrase: credentials.passphrase??"");
path: path,
password: credentials.password!,
language: credentials.language,
passphrase: credentials.passphrase ?? "");
final wallet = MoneroWallet(
walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource,
@ -145,7 +171,8 @@ class MoneroWalletService extends WalletService<
}
@override
Future<MoneroWallet> openWallet(String name, String password, {OpenWalletTry openWalletTry = OpenWalletTry.initial}) async {
Future<MoneroWallet> openWallet(String name, String password,
{OpenWalletTry openWalletTry = OpenWalletTry.initial}) async {
try {
final path = await pathForWallet(name: name, type: getType());
@ -303,8 +330,28 @@ class MoneroWalletService extends WalletService<
rethrow;
}
try {
if (isBip39Seed(credentials.mnemonic)) {
final path =
await pathForWallet(name: credentials.name, type: getType());
return _restoreFromBip39(
path: path,
password: credentials.password!,
mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!,
overrideHeight: credentials.height!,
passphrase: credentials.passphrase,
);
}
} catch (e) {
printV("Bip39 restore failed: $e");
rethrow;
}
try {
final path = await pathForWallet(name: credentials.name, type: getType());
monero_wallet_manager.restoreFromSeed(
path: path,
password: credentials.password!,
@ -325,6 +372,50 @@ class MoneroWalletService extends WalletService<
}
}
Future<MoneroWallet> _restoreFromBip39({
required String path,
required String password,
required String mnemonic,
required WalletInfo walletInfo,
String? passphrase,
int? overrideHeight,
}) async {
walletInfo.derivationInfo = DerivationInfo(
derivationType: DerivationType.bip39,
derivationPath: "m/44'/128'/0'/0/0",
);
final legacyMnemonic =
getLegacySeedFromBip39(mnemonic, passphrase: passphrase ?? "");
final height =
overrideHeight ?? getMoneroHeigthByDate(date: DateTime.now());
walletInfo.isRecovery = true;
walletInfo.restoreHeight = height;
monero_wallet_manager.restoreFromSeed(
path: path,
password: password,
passphrase: '',
seed: legacyMnemonic,
restoreHeight: height,
);
monero.Wallet_setCacheAttribute(wptr!,
key: "cakewallet.seed.bip39", value: mnemonic);
monero.Wallet_store(wptr!);
final wallet = MoneroWallet(
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
password: password,
);
await wallet.init();
return wallet;
}
Future<MoneroWallet> restoreFromPolyseed(
MoneroRestoreWalletFromSeedCredentials credentials) async {
try {
@ -344,23 +435,21 @@ class MoneroWalletService extends WalletService<
}
}
Future<MoneroWallet> _restoreFromPolyseed(
String path, String password, Polyseed polyseed, WalletInfo walletInfo, PolyseedLang lang,
Future<MoneroWallet> _restoreFromPolyseed(String path, String password,
Polyseed polyseed, WalletInfo walletInfo, PolyseedLang lang,
{PolyseedCoin coin = PolyseedCoin.POLYSEED_MONERO,
int? overrideHeight,
String? passphrase}) async {
if (polyseed.isEncrypted == false &&
(passphrase??'') != "") {
if (polyseed.isEncrypted == false && (passphrase ?? '') != "") {
// Fallback to the different passphrase offset method, when a passphrase
// was provided but the polyseed is not encrypted.
monero_wallet_manager.restoreWalletFromPolyseedWithOffset(
path: path,
password: password,
seed: polyseed.encode(lang, coin),
seedOffset: passphrase??'',
language: "English");
path: path,
password: password,
seed: polyseed.encode(lang, coin),
seedOffset: passphrase ?? '',
language: "English");
final wallet = MoneroWallet(
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
@ -437,7 +526,8 @@ class MoneroWalletService extends WalletService<
if (walletFilesExist(path)) await repairOldAndroidWallet(name);
await monero_wallet_manager.openWalletAsync({'path': path, 'password': password});
await monero_wallet_manager
.openWalletAsync({'path': path, 'password': password});
final walletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
final wallet = MoneroWallet(

View file

@ -5,18 +5,23 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: "4897882604d919befd350648c7f91926a9d5de99e67b455bf0917cc2362f4bb8"
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
url: "https://pub.dev"
source: hosted
version: "47.0.0"
version: "76.0.0"
_macros:
dependency: transitive
description: dart
source: sdk
version: "0.3.3"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "690e335554a8385bc9d787117d9eb52c0c03ee207a607e593de3c9d71b1cfe80"
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
url: "https://pub.dev"
source: hosted
version: "4.7.0"
version: "6.11.0"
args:
dependency: transitive
description:
@ -41,6 +46,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.11.0"
bip32:
dependency: "direct main"
description:
name: bip32
sha256: "54787cd7a111e9d37394aabbf53d1fc5e2e0e0af2cd01c459147a97c0e3f8a97"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
bip39:
dependency: "direct main"
description:
name: bip39
sha256: de1ee27ebe7d96b84bb3a04a4132a0a3007dcdd5ad27dd14aa87a29d97c45edc
url: "https://pub.dev"
source: hosted
version: "1.0.6"
blockchain_utils:
dependency: transitive
description:
@ -66,6 +87,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.1"
bs58check:
dependency: transitive
description:
name: bs58check
sha256: c4a164d42b25c2f6bc88a8beccb9fc7d01440f3c60ba23663a20a70faf484ea9
url: "https://pub.dev"
source: hosted
version: "1.0.2"
build:
dependency: transitive
description:
@ -94,10 +123,10 @@ packages:
dependency: "direct dev"
description:
name: build_resolvers
sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6"
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
url: "https://pub.dev"
source: hosted
version: "2.0.10"
version: "2.4.4"
build_runner:
dependency: "direct dev"
description:
@ -222,10 +251,10 @@ packages:
dependency: transitive
description:
name: dart_style
sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4"
sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820"
url: "https://pub.dev"
source: hosted
version: "2.2.4"
version: "2.3.8"
dbus:
dependency: transitive
description:
@ -348,6 +377,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.6.0"
hex:
dependency: transitive
description:
name: hex
sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a"
url: "https://pub.dev"
source: hosted
version: "0.2.0"
hive:
dependency: transitive
description:
@ -360,10 +397,10 @@ packages:
dependency: "direct dev"
description:
name: hive_generator
sha256: "81fd20125cb2ce8fd23623d7744ffbaf653aae93706c9bd3bf7019ea0ace3938"
sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4"
url: "https://pub.dev"
source: hosted
version: "1.1.3"
version: "2.0.1"
http:
dependency: "direct main"
description:
@ -468,6 +505,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.0"
macros:
dependency: transitive
description:
name: macros
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
url: "https://pub.dev"
source: hosted
version: "0.1.3-main.0"
matcher:
dependency: transitive
description:
@ -512,10 +557,18 @@ packages:
dependency: "direct dev"
description:
name: mobx_codegen
sha256: d4beb9cea4b7b014321235f8fdc7c2193ee0fe1d1198e9da7403f8bc85c4407c
sha256: "990da80722f7d7c0017dec92040b31545d625b15d40204c36a1e63d167c73cdc"
url: "https://pub.dev"
source: hosted
version: "2.3.0"
version: "2.7.0"
mockito:
dependency: "direct dev"
description:
name: mockito
sha256: f99d8d072e249f719a5531735d146d8cf04c580d93920b04de75bef6dfb2daf6
url: "https://pub.dev"
source: hosted
version: "5.4.5"
monero:
dependency: "direct main"
description:
@ -735,18 +788,18 @@ packages:
dependency: transitive
description:
name: source_gen
sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d"
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
url: "https://pub.dev"
source: hosted
version: "1.2.6"
version: "1.5.0"
source_helper:
dependency: transitive
description:
name: source_helper
sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f"
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
url: "https://pub.dev"
source: hosted
version: "1.3.3"
version: "1.3.5"
source_span:
dependency: transitive
description:
@ -924,5 +977,5 @@ packages:
source: hosted
version: "3.1.3"
sdks:
dart: ">=3.5.0 <4.0.0"
dart: ">=3.6.0 <4.0.0"
flutter: ">=3.24.0"

View file

@ -12,6 +12,8 @@ environment:
dependencies:
flutter:
sdk: flutter
bip39: ^1.0.6
bip32: ^2.0.0
ffi: ^2.0.1
http: ^1.1.0
path_provider: ^2.0.11
@ -36,7 +38,8 @@ dev_dependencies:
build_runner: ^2.4.7
build_resolvers: ^2.0.9
mobx_codegen: ^2.0.7
hive_generator: ^1.1.3
mockito: ^5.4.5
hive_generator: ^2.0.1
dependency_overrides:
watcher: ^1.1.0

View file

@ -0,0 +1,39 @@
import 'package:cw_monero/bip39_seed.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group("Exodus Style bip39", () {
group("Test Wallet 1", () {
final bip39Seed = 'meadow tip best belt boss eyebrow control affair eternal piece very shiver';
final expectedLegacySeed0 = "tasked eight afraid laboratory tail feline rift reinvest vane cafe bailed foggy dormant paper jigsaw king hazard suture king dapper dummy jolted dating dwindling king";
final expectedLegacySeed1 = "palace pairing axes mohawk rekindle excess awful juvenile shipped talent nibs efficient dapper biggest swung fight pact innocent emerge issued titans affair nearby noises emerge";
test("Get legacy Seed from bip39", () {
final legacySeed = getLegacySeedFromBip39(bip39Seed);
expect(legacySeed, expectedLegacySeed0);
});
test("Get legacy Seed from bip39 with account index", () {
final legacySeed = getLegacySeedFromBip39(bip39Seed, accountIndex: 1);
expect(legacySeed, expectedLegacySeed1);
});
});
group("Test Wallet 2", () {
final bip39Seed = "color ranch color remove subway public water embrace before begin liberty fault";
final expectedLegacySeed0 = "somewhere problems gauze gigantic intended foxes upcoming saved waffle pipeline lurk bogeys empty wipeout abbey italics novelty tucks rafts elite lunar obnoxious awful bugs elite";
final expectedLegacySeed1 = "playful toxic wildly eluded mesh fainted february mugged maps repent vigilant hitched seventh threaten clue fetches sample diet number alkaline future cottage tuition vegan alkaline";
test("Get legacy Seed from bip39", () {
final legacySeed = getLegacySeedFromBip39(bip39Seed);
expect(legacySeed, expectedLegacySeed0);
});
test("Get legacy Seed from bip39 with account index", () {
final legacySeed = getLegacySeedFromBip39(bip39Seed, accountIndex: 1);
expect(legacySeed, expectedLegacySeed1);
});
});
});
}

View file

@ -0,0 +1,29 @@
import 'package:mockito/mockito.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
class MockPathProviderPlatform extends Mock
with MockPlatformInterfaceMixin
implements PathProviderPlatform {
Future<String> getTemporaryPath() => throw UnimplementedError();
Future<String> getApplicationSupportPath() => throw UnimplementedError();
Future<String> getLibraryPath() => throw UnimplementedError();
Future<String> getApplicationDocumentsPath() async => "./test/data";
Future<String> getExternalStoragePath() => throw UnimplementedError();
Future<List<String>> getExternalCachePaths() => throw UnimplementedError();
Future<String> getDownloadsPath() => throw UnimplementedError();
@override
Future<String?> getApplicationCachePath() => throw UnimplementedError();
@override
Future<List<String>?> getExternalStoragePaths({StorageDirectory? type}) =>
throw UnimplementedError();
}

View file

@ -0,0 +1,148 @@
import 'dart:io';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cw_monero/monero_wallet_service.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:hive/hive.dart';
import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
import 'mock/path_provider.dart';
import 'utils/setup_monero_c.dart';
Future<void> main() async {
group("MoneroWalletService Tests", () {
Hive.init('./test/data/db');
late MoneroWalletService walletService;
late File moneroCBinary;
setUpAll(() async {
PathProviderPlatform.instance = MockPathProviderPlatform();
final Box<WalletInfo> walletInfoSource =
await Hive.openBox('testWalletInfo');
final Box<UnspentCoinsInfo> unspentCoinsInfoSource =
await Hive.openBox('testUnspentCoinsInfo');
walletService = MoneroWalletService(walletInfoSource, unspentCoinsInfoSource);
moneroCBinary = getMoneroCBinary().copySync(moneroCBinaryName);
});
tearDownAll(() {
Directory('./test/data').deleteSync(recursive: true);
moneroCBinary.deleteSync();
});
group("Create wallet", () {
test("Create Legacy Wallet", () async {
final credentials = _getTestCreateCredentials(
name: 'Create Wallet LS',
language: 'English',
seedType: MoneroSeedType.legacy);
final wallet = await walletService.create(credentials);
expect(wallet.seed.split(" ").length, 25);
expect(wallet.restoreHeight, greaterThan(3000000));
});
test("Create Polyseed Wallet", () async {
final credentials = _getTestCreateCredentials(
name: 'Create Wallet PS',
language: 'English',
seedType: MoneroSeedType.polyseed);
final wallet = await walletService.create(credentials);
expect(wallet.seed.split(" ").length, 16);
expect(wallet.restoreHeight, greaterThan(3000000));
});
test("Create Bip39 Wallet", () async {
final credentials = _getTestCreateCredentials(
name: 'Create Wallet BS',
language: 'English',
seedType: MoneroSeedType.bip39);
final wallet = await walletService.create(credentials);
expect(wallet.seed.split(" ").length, 12);
expect(wallet.restoreHeight, greaterThan(3000000));
});
});
group("Restore wallet", () {
test('Legacy Seed', () async {
final credentials = _getTestRestoreCredentials(
name: 'Test Wallet LS',
mnemonic:
'ability pockets lordship tomorrow gypsy match neutral uncle avatar betting bicycle junk unzip pyramid lynx mammal edgy empty uneven knowledge juvenile wiring paradise psychic betting',
);
final wallet = await walletService.restoreFromSeed(credentials);
expect(wallet.walletAddresses.primaryAddress,
'48tLyQXpcwt8w6uKHyb5Zs3vdnoDWAEKFQr1c198o7aX9dBzXP3BTSMVsDiuH3ozDCNqwojb4vNeQZf7xg6URimDLaNtGSN');
});
test('Bip39 Seed', () async {
final credentials = _getTestRestoreCredentials(
name: 'Test Wallet BS',
mnemonic:
'color ranch color remove subway public water embrace before begin liberty fault');
final wallet = await walletService.restoreFromSeed(credentials);
expect(wallet.walletAddresses.primaryAddress,
'49MggvPosJugF8Zq7WAKbsSchz6vbyL6YiUxM4ryfGQDXphs6wiWiXLFWCSshnLPcceGTWUaKfWWMHQAAKESV3TQJVQsL9a');
});
});
});
}
MoneroRestoreWalletFromSeedCredentials _getTestRestoreCredentials({
required String name,
required String mnemonic,
}) {
final credentials = MoneroRestoreWalletFromSeedCredentials(
name: name, mnemonic: mnemonic, passphrase: '', password: "test");
credentials.walletInfo = WalletInfo.external(
id: WalletBase.idFor(name, WalletType.monero),
name: name,
type: WalletType.monero,
isRecovery: true,
restoreHeight: credentials.height ?? 0,
date: DateTime.now(),
path: '',
dirPath: '',
address: '',
);
return credentials;
}
MoneroNewWalletCredentials _getTestCreateCredentials({
required String name,
required String language,
required MoneroSeedType seedType,
String? mnemonic,
}) {
final credentials = MoneroNewWalletCredentials(
name: name,
language: language,
seedType: seedType,
password: "test",
mnemonic: mnemonic,
passphrase: '',
);
credentials.walletInfo = WalletInfo.external(
id: WalletBase.idFor(name, WalletType.monero),
name: name,
type: WalletType.monero,
isRecovery: false,
restoreHeight: credentials.height ?? 0,
date: DateTime.now(),
path: '',
dirPath: '',
address: '',
);
return credentials;
}

View file

@ -0,0 +1,16 @@
import 'dart:io';
File getMoneroCBinary() {
if (Platform.isWindows)
return File(
'../scripts/monero_c/release/monero/x86_64-w64-mingw32_libwallet2_api_c.dll');
if (Platform.isMacOS) return File('../macos/monero_libwallet2_api_c.dylib');
return File('../scripts/monero_c/release/monero/x86_64-linux-gnu_libwallet2_api_c.so');
}
String get moneroCBinaryName {
if (Platform.isWindows)
return "monero_libwallet2_api_c.dll";
if (Platform.isMacOS) return "monero_libwallet2_api_c.dylib";
return "/usr/lib/monero_libwallet2_api_c.so";
}