From 65402ba1eb88c379e19f34f6316897e08df6941f Mon Sep 17 00:00:00 2001 From: JoeGruffins <34998433+JoeGruffins@users.noreply.github.com> Date: Tue, 17 Jun 2025 07:37:49 +0900 Subject: [PATCH] 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 --- cw_decred/lib/wallet.dart | 15 +++-- cw_decred/lib/wallet_service.dart | 99 +++++++++++++++++++++++++------ scripts/android/build_decred.sh | 2 +- scripts/ios/build_decred.sh | 2 +- scripts/macos/build_decred.sh | 2 +- 5 files changed, 92 insertions(+), 28 deletions(-) diff --git a/cw_decred/lib/wallet.dart b/cw_decred/lib/wallet.dart index ac70a4aaa..97aee775d 100644 --- a/cw_decred/lib/wallet.dart +++ b/cw_decred/lib/wallet.dart @@ -5,6 +5,8 @@ import 'package:path/path.dart' as p; import 'package:cw_core/exceptions.dart'; import 'package:cw_core/transaction_direction.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/pending_transaction.dart'; import 'package:cw_decred/transaction_credentials.dart'; @@ -307,9 +309,10 @@ abstract class DecredWalletBase persistantPeer = addr; await _libwallet.closeWallet(walletInfo.name); final network = isTestnet ? "testnet" : "mainnet"; + final dirPath = await pathForWalletDir(name: walletInfo.name, type: WalletType.decred); final config = { "name": walletInfo.name, - "datadir": walletInfo.dirPath, + "datadir": dirPath, "net": network, "unsyncedaddrs": true, }; @@ -605,22 +608,22 @@ abstract class DecredWalletBase final sourceDir = Directory(currentDirPath); final targetDir = Directory(newDirPath); - + if (!targetDir.existsSync()) { await targetDir.create(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); - + if (entity is File) { await entity.rename(targetPath); } else if (entity is Directory) { await Directory(targetPath).create(recursive: true); } } - + await sourceDir.delete(recursive: true); } diff --git a/cw_decred/lib/wallet_service.dart b/cw_decred/lib/wallet_service.dart index e2313904e..93c708886 100644 --- a/cw_decred/lib/wallet_service.dart +++ b/cw_decred/lib/wallet_service.dart @@ -8,6 +8,7 @@ import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_type.dart'; +import 'package:path/path.dart'; import 'package:hive/hive.dart'; import 'package:collection/collection.dart'; import 'package:cw_core/unspent_coins_info.dart'; @@ -57,42 +58,93 @@ class DecredWalletService extends WalletService< @override Future create(DecredNewWalletCredentials credentials, {bool? isTestnet}) async { await this.init(); + final dirPath = await pathForWalletDir(name: credentials.walletInfo!.name, type: getType()); + final network = isTestnet == true ? testnet : mainnet; final config = { "name": credentials.walletInfo!.name, - "datadir": credentials.walletInfo!.dirPath, + "datadir": dirPath, "pass": credentials.password!, - "net": isTestnet == true ? testnet : mainnet, + "net": network, "unsyncedaddrs": true, }; await libwallet!.createWallet(jsonEncode(config)); final di = DerivationInfo( derivationPath: isTestnet == true ? seedRestorePathTestnet : seedRestorePath); 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!, this.unspentCoinsInfoSource, libwallet!, closeLibwallet); await wallet.init(); 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 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 Future openWallet(String name, String password) async { final walletInfo = walletInfoSource.values .firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!; - final network = walletInfo.derivationInfo?.derivationPath == seedRestorePathTestnet || - walletInfo.derivationInfo?.derivationPath == pubkeyRestorePathTestnet - ? testnet - : mainnet; + if (walletInfo.network == null || walletInfo.network == "") { + walletInfo.network = walletInfo.derivationInfo?.derivationPath == seedRestorePathTestnet || + walletInfo.derivationInfo?.derivationPath == pubkeyRestorePathTestnet + ? testnet + : mainnet; + } await this.init(); - final walletDirExists = Directory(walletInfo.dirPath).existsSync(); - if (!walletDirExists) { - walletInfo.dirPath = await pathForWalletDir(name: name, type: getType()); + + // Cake wallet version 4.27.0 and earlier gave a wallet dir that did not + // 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 = { - "name": walletInfo.name, - "datadir": walletInfo.dirPath, - "net": network, + "name": name, + "datadir": dirPath, + "net": walletInfo.network, "unsyncedaddrs": true, }; await libwallet!.loadWallet(jsonEncode(config)); @@ -127,12 +179,11 @@ class DecredWalletService extends WalletService< await currentWallet.renameWalletFiles(newName); - final newDirPath = await pathForWalletDir(name: newName, type: getType()); final newWalletInfo = currentWalletInfo; newWalletInfo.id = WalletBase.idFor(newName, getType()); newWalletInfo.name = newName; - newWalletInfo.dirPath = newDirPath; - newWalletInfo.network = network; + newWalletInfo.dirPath = ""; + newWalletInfo.path = ""; await walletInfoSource.put(currentWalletInfo.key, newWalletInfo); } @@ -141,18 +192,23 @@ class DecredWalletService extends WalletService< Future restoreFromSeed(DecredRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async { await this.init(); + final network = isTestnet == true ? testnet : mainnet; + final dirPath = await pathForWalletDir(name: credentials.walletInfo!.name, type: getType()); final config = { "name": credentials.walletInfo!.name, - "datadir": credentials.walletInfo!.dirPath, + "datadir": dirPath, "pass": credentials.password!, "mnemonic": credentials.mnemonic, - "net": isTestnet == true ? testnet : mainnet, + "net": network, "unsyncedaddrs": true, }; await libwallet!.createWallet(jsonEncode(config)); final di = DerivationInfo( derivationPath: isTestnet == true ? seedRestorePathTestnet : seedRestorePath); credentials.walletInfo!.derivationInfo = di; + credentials.walletInfo!.network = network; + credentials.walletInfo!.dirPath = ""; + credentials.walletInfo!.path = ""; final wallet = DecredWallet(credentials.walletInfo!, credentials.password!, this.unspentCoinsInfoSource, libwallet!, closeLibwallet); await wallet.init(); @@ -165,17 +221,22 @@ class DecredWalletService extends WalletService< Future restoreFromKeys(DecredRestoreWalletFromPubkeyCredentials credentials, {bool? isTestnet}) async { await this.init(); + final network = isTestnet == true ? testnet : mainnet; + final dirPath = await pathForWalletDir(name: credentials.walletInfo!.name, type: getType()); final config = { "name": credentials.walletInfo!.name, - "datadir": credentials.walletInfo!.dirPath, + "datadir": dirPath, "pubkey": credentials.pubkey, - "net": isTestnet == true ? testnet : mainnet, + "net": network, "unsyncedaddrs": true, }; await libwallet!.createWatchOnlyWallet(jsonEncode(config)); final di = DerivationInfo( derivationPath: isTestnet == true ? pubkeyRestorePathTestnet : pubkeyRestorePath); credentials.walletInfo!.derivationInfo = di; + credentials.walletInfo!.network = network; + credentials.walletInfo!.dirPath = ""; + credentials.walletInfo!.path = ""; final wallet = DecredWallet(credentials.walletInfo!, credentials.password!, this.unspentCoinsInfoSource, libwallet!, closeLibwallet); await wallet.init(); diff --git a/scripts/android/build_decred.sh b/scripts/android/build_decred.sh index 75ed45aca..ce37a7353 100755 --- a/scripts/android/build_decred.sh +++ b/scripts/android/build_decred.sh @@ -7,7 +7,7 @@ cd "$(dirname "$0")" CW_DECRED_DIR=$(realpath ../..)/cw_decred LIBWALLET_PATH="${PWD}/decred/libwallet" LIBWALLET_URL="https://github.com/decred/libwallet.git" -LIBWALLET_VERSION="dba5327d35cb5d5d1ff113b780869deee154511f" +LIBWALLET_VERSION="05f8d7374999400fe4d525eb365c39b77d307b14" if [[ -e $LIBWALLET_PATH ]]; then rm -fr $LIBWALLET_PATH || true diff --git a/scripts/ios/build_decred.sh b/scripts/ios/build_decred.sh index 6860c7776..37384f4e1 100755 --- a/scripts/ios/build_decred.sh +++ b/scripts/ios/build_decred.sh @@ -3,7 +3,7 @@ set -e . ./config.sh LIBWALLET_PATH="${EXTERNAL_IOS_SOURCE_DIR}/libwallet" LIBWALLET_URL="https://github.com/decred/libwallet.git" -LIBWALLET_VERSION="dba5327d35cb5d5d1ff113b780869deee154511f" +LIBWALLET_VERSION="05f8d7374999400fe4d525eb365c39b77d307b14" if [[ -e $LIBWALLET_PATH ]]; then rm -fr $LIBWALLET_PATH diff --git a/scripts/macos/build_decred.sh b/scripts/macos/build_decred.sh index e7e5d492f..b1bbfbc43 100755 --- a/scripts/macos/build_decred.sh +++ b/scripts/macos/build_decred.sh @@ -4,7 +4,7 @@ LIBWALLET_PATH="${EXTERNAL_MACOS_SOURCE_DIR}/libwallet" LIBWALLET_URL="https://github.com/decred/libwallet.git" -LIBWALLET_VERSION="dba5327d35cb5d5d1ff113b780869deee154511f" +LIBWALLET_VERSION="05f8d7374999400fe4d525eb365c39b77d307b14" echo "======================= DECRED LIBWALLET ========================="