dcr: Always fetch the current dir path. (#2242)

* dcr: Always fetch the current dir path.

On ios devices the path will change between updates breaking decred.
Never save the path and always check to ensure it is up to date.
Previous wallets were also not creating a directory in the correct
place. Move those when found.

* Update cw_decred/lib/wallet_service.dart

* dcr: Update libwallet dep.

---------

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
JoeGruffins 2025-06-17 07:37:49 +09:00 committed by GitHub
parent 17d99e5451
commit 65402ba1eb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 92 additions and 28 deletions

View file

@ -5,6 +5,8 @@ import 'package:path/path.dart' as p;
import 'package:cw_core/exceptions.dart'; import 'package:cw_core/exceptions.dart';
import 'package:cw_core/transaction_direction.dart'; import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cw_decred/amount_format.dart'; import 'package:cw_decred/amount_format.dart';
import 'package:cw_decred/pending_transaction.dart'; import 'package:cw_decred/pending_transaction.dart';
import 'package:cw_decred/transaction_credentials.dart'; import 'package:cw_decred/transaction_credentials.dart';
@ -307,9 +309,10 @@ abstract class DecredWalletBase
persistantPeer = addr; persistantPeer = addr;
await _libwallet.closeWallet(walletInfo.name); await _libwallet.closeWallet(walletInfo.name);
final network = isTestnet ? "testnet" : "mainnet"; final network = isTestnet ? "testnet" : "mainnet";
final dirPath = await pathForWalletDir(name: walletInfo.name, type: WalletType.decred);
final config = { final config = {
"name": walletInfo.name, "name": walletInfo.name,
"datadir": walletInfo.dirPath, "datadir": dirPath,
"net": network, "net": network,
"unsyncedaddrs": true, "unsyncedaddrs": true,
}; };
@ -605,22 +608,22 @@ abstract class DecredWalletBase
final sourceDir = Directory(currentDirPath); final sourceDir = Directory(currentDirPath);
final targetDir = Directory(newDirPath); final targetDir = Directory(newDirPath);
if (!targetDir.existsSync()) { if (!targetDir.existsSync()) {
await targetDir.create(recursive: true); await targetDir.create(recursive: true);
} }
await for (final entity in sourceDir.list(recursive: true)) { await for (final entity in sourceDir.list(recursive: true)) {
final relativePath = entity.path.substring(sourceDir.path.length+1); final relativePath = entity.path.substring(sourceDir.path.length + 1);
final targetPath = p.join(targetDir.path, relativePath); final targetPath = p.join(targetDir.path, relativePath);
if (entity is File) { if (entity is File) {
await entity.rename(targetPath); await entity.rename(targetPath);
} else if (entity is Directory) { } else if (entity is Directory) {
await Directory(targetPath).create(recursive: true); await Directory(targetPath).create(recursive: true);
} }
} }
await sourceDir.delete(recursive: true); await sourceDir.delete(recursive: true);
} }

View file

@ -8,6 +8,7 @@ import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:path/path.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/unspent_coins_info.dart';
@ -57,42 +58,93 @@ class DecredWalletService extends WalletService<
@override @override
Future<DecredWallet> create(DecredNewWalletCredentials credentials, {bool? isTestnet}) async { Future<DecredWallet> create(DecredNewWalletCredentials credentials, {bool? isTestnet}) async {
await this.init(); await this.init();
final dirPath = await pathForWalletDir(name: credentials.walletInfo!.name, type: getType());
final network = isTestnet == true ? testnet : mainnet;
final config = { final config = {
"name": credentials.walletInfo!.name, "name": credentials.walletInfo!.name,
"datadir": credentials.walletInfo!.dirPath, "datadir": dirPath,
"pass": credentials.password!, "pass": credentials.password!,
"net": isTestnet == true ? testnet : mainnet, "net": network,
"unsyncedaddrs": true, "unsyncedaddrs": true,
}; };
await libwallet!.createWallet(jsonEncode(config)); await libwallet!.createWallet(jsonEncode(config));
final di = DerivationInfo( final di = DerivationInfo(
derivationPath: isTestnet == true ? seedRestorePathTestnet : seedRestorePath); derivationPath: isTestnet == true ? seedRestorePathTestnet : seedRestorePath);
credentials.walletInfo!.derivationInfo = di; credentials.walletInfo!.derivationInfo = di;
credentials.walletInfo!.network = network;
// ios will move our wallet directory when updating. Since we must
// recalculate the new path every time we open the wallet, ensure this path
// is not used. An older wallet will have a directory here which is a
// condition for moving the wallet when opening, so this must be kept blank
// going forward.
credentials.walletInfo!.dirPath = "";
credentials.walletInfo!.path = "";
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!, final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
this.unspentCoinsInfoSource, libwallet!, closeLibwallet); this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
await wallet.init(); await wallet.init();
return wallet; return wallet;
} }
void copyDirectorySync(Directory source, Directory destination) {
/// create destination folder if not exist
if (!destination.existsSync()) {
destination.createSync(recursive: true);
}
/// get all files from source (recursive: false is important here)
source.listSync(recursive: false).forEach((entity) {
final newPath = destination.path + Platform.pathSeparator + basename(entity.path);
if (entity is File) {
entity.rename(newPath);
} else if (entity is Directory) {
copyDirectorySync(entity, Directory(newPath));
}
});
}
Future<void> moveWallet(String fromPath, String toPath) async {
final oldWalletDir = new Directory(fromPath);
final newWalletDir = new Directory(toPath);
copyDirectorySync(oldWalletDir, newWalletDir);
// It would be ideal to delete the old directory here, but ios will error
// sometimes with "OS Error: No such file or directory, errno = 2" even
// after checking if it exists.
}
@override @override
Future<DecredWallet> openWallet(String name, String password) async { Future<DecredWallet> openWallet(String name, String password) async {
final walletInfo = walletInfoSource.values final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!; .firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
final network = walletInfo.derivationInfo?.derivationPath == seedRestorePathTestnet || if (walletInfo.network == null || walletInfo.network == "") {
walletInfo.derivationInfo?.derivationPath == pubkeyRestorePathTestnet walletInfo.network = walletInfo.derivationInfo?.derivationPath == seedRestorePathTestnet ||
? testnet walletInfo.derivationInfo?.derivationPath == pubkeyRestorePathTestnet
: mainnet; ? testnet
: mainnet;
}
await this.init(); await this.init();
final walletDirExists = Directory(walletInfo.dirPath).existsSync();
if (!walletDirExists) { // Cake wallet version 4.27.0 and earlier gave a wallet dir that did not
walletInfo.dirPath = await pathForWalletDir(name: name, type: getType()); // match the name. Move those to the correct place.
final dirPath = await pathForWalletDir(name: name, type: getType());
if (walletInfo.path != "") {
// On ios the stored dir no longer exists. We can only trust the basename.
// dirPath may already be updated and lost the basename, so look at path.
final randomBasename = basename(walletInfo.path);
final oldDir = await pathForWalletDir(name: randomBasename, type: getType());
if (oldDir != dirPath) {
await this.moveWallet(oldDir, dirPath);
}
// Clear the path so this does not trigger again.
walletInfo.dirPath = "";
walletInfo.path = "";
await walletInfo.save();
} }
final config = { final config = {
"name": walletInfo.name, "name": name,
"datadir": walletInfo.dirPath, "datadir": dirPath,
"net": network, "net": walletInfo.network,
"unsyncedaddrs": true, "unsyncedaddrs": true,
}; };
await libwallet!.loadWallet(jsonEncode(config)); await libwallet!.loadWallet(jsonEncode(config));
@ -127,12 +179,11 @@ class DecredWalletService extends WalletService<
await currentWallet.renameWalletFiles(newName); await currentWallet.renameWalletFiles(newName);
final newDirPath = await pathForWalletDir(name: newName, type: getType());
final newWalletInfo = currentWalletInfo; final newWalletInfo = currentWalletInfo;
newWalletInfo.id = WalletBase.idFor(newName, getType()); newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName; newWalletInfo.name = newName;
newWalletInfo.dirPath = newDirPath; newWalletInfo.dirPath = "";
newWalletInfo.network = network; newWalletInfo.path = "";
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo); await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
} }
@ -141,18 +192,23 @@ class DecredWalletService extends WalletService<
Future<DecredWallet> restoreFromSeed(DecredRestoreWalletFromSeedCredentials credentials, Future<DecredWallet> restoreFromSeed(DecredRestoreWalletFromSeedCredentials credentials,
{bool? isTestnet}) async { {bool? isTestnet}) async {
await this.init(); await this.init();
final network = isTestnet == true ? testnet : mainnet;
final dirPath = await pathForWalletDir(name: credentials.walletInfo!.name, type: getType());
final config = { final config = {
"name": credentials.walletInfo!.name, "name": credentials.walletInfo!.name,
"datadir": credentials.walletInfo!.dirPath, "datadir": dirPath,
"pass": credentials.password!, "pass": credentials.password!,
"mnemonic": credentials.mnemonic, "mnemonic": credentials.mnemonic,
"net": isTestnet == true ? testnet : mainnet, "net": network,
"unsyncedaddrs": true, "unsyncedaddrs": true,
}; };
await libwallet!.createWallet(jsonEncode(config)); await libwallet!.createWallet(jsonEncode(config));
final di = DerivationInfo( final di = DerivationInfo(
derivationPath: isTestnet == true ? seedRestorePathTestnet : seedRestorePath); derivationPath: isTestnet == true ? seedRestorePathTestnet : seedRestorePath);
credentials.walletInfo!.derivationInfo = di; credentials.walletInfo!.derivationInfo = di;
credentials.walletInfo!.network = network;
credentials.walletInfo!.dirPath = "";
credentials.walletInfo!.path = "";
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!, final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
this.unspentCoinsInfoSource, libwallet!, closeLibwallet); this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
await wallet.init(); await wallet.init();
@ -165,17 +221,22 @@ class DecredWalletService extends WalletService<
Future<DecredWallet> restoreFromKeys(DecredRestoreWalletFromPubkeyCredentials credentials, Future<DecredWallet> restoreFromKeys(DecredRestoreWalletFromPubkeyCredentials credentials,
{bool? isTestnet}) async { {bool? isTestnet}) async {
await this.init(); await this.init();
final network = isTestnet == true ? testnet : mainnet;
final dirPath = await pathForWalletDir(name: credentials.walletInfo!.name, type: getType());
final config = { final config = {
"name": credentials.walletInfo!.name, "name": credentials.walletInfo!.name,
"datadir": credentials.walletInfo!.dirPath, "datadir": dirPath,
"pubkey": credentials.pubkey, "pubkey": credentials.pubkey,
"net": isTestnet == true ? testnet : mainnet, "net": network,
"unsyncedaddrs": true, "unsyncedaddrs": true,
}; };
await libwallet!.createWatchOnlyWallet(jsonEncode(config)); await libwallet!.createWatchOnlyWallet(jsonEncode(config));
final di = DerivationInfo( final di = DerivationInfo(
derivationPath: isTestnet == true ? pubkeyRestorePathTestnet : pubkeyRestorePath); derivationPath: isTestnet == true ? pubkeyRestorePathTestnet : pubkeyRestorePath);
credentials.walletInfo!.derivationInfo = di; credentials.walletInfo!.derivationInfo = di;
credentials.walletInfo!.network = network;
credentials.walletInfo!.dirPath = "";
credentials.walletInfo!.path = "";
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!, final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
this.unspentCoinsInfoSource, libwallet!, closeLibwallet); this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
await wallet.init(); await wallet.init();

View file

@ -7,7 +7,7 @@ cd "$(dirname "$0")"
CW_DECRED_DIR=$(realpath ../..)/cw_decred CW_DECRED_DIR=$(realpath ../..)/cw_decred
LIBWALLET_PATH="${PWD}/decred/libwallet" LIBWALLET_PATH="${PWD}/decred/libwallet"
LIBWALLET_URL="https://github.com/decred/libwallet.git" LIBWALLET_URL="https://github.com/decred/libwallet.git"
LIBWALLET_VERSION="dba5327d35cb5d5d1ff113b780869deee154511f" LIBWALLET_VERSION="05f8d7374999400fe4d525eb365c39b77d307b14"
if [[ -e $LIBWALLET_PATH ]]; then if [[ -e $LIBWALLET_PATH ]]; then
rm -fr $LIBWALLET_PATH || true rm -fr $LIBWALLET_PATH || true

View file

@ -3,7 +3,7 @@ set -e
. ./config.sh . ./config.sh
LIBWALLET_PATH="${EXTERNAL_IOS_SOURCE_DIR}/libwallet" LIBWALLET_PATH="${EXTERNAL_IOS_SOURCE_DIR}/libwallet"
LIBWALLET_URL="https://github.com/decred/libwallet.git" LIBWALLET_URL="https://github.com/decred/libwallet.git"
LIBWALLET_VERSION="dba5327d35cb5d5d1ff113b780869deee154511f" LIBWALLET_VERSION="05f8d7374999400fe4d525eb365c39b77d307b14"
if [[ -e $LIBWALLET_PATH ]]; then if [[ -e $LIBWALLET_PATH ]]; then
rm -fr $LIBWALLET_PATH rm -fr $LIBWALLET_PATH

View file

@ -4,7 +4,7 @@
LIBWALLET_PATH="${EXTERNAL_MACOS_SOURCE_DIR}/libwallet" LIBWALLET_PATH="${EXTERNAL_MACOS_SOURCE_DIR}/libwallet"
LIBWALLET_URL="https://github.com/decred/libwallet.git" LIBWALLET_URL="https://github.com/decred/libwallet.git"
LIBWALLET_VERSION="dba5327d35cb5d5d1ff113b780869deee154511f" LIBWALLET_VERSION="05f8d7374999400fe4d525eb365c39b77d307b14"
echo "======================= DECRED LIBWALLET =========================" echo "======================= DECRED LIBWALLET ========================="