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>
JoeGruffins committed
Jun 17, 2025 at 07:37 UTC
65402ba1eb88c379e19f34f6316897e08df6941f
5 files changed
+92
-28
cw_decred/lib/wallet.dart
+9
-6
@@ -5,6 +5,8 @@ import 'package:path/path.dart' as p;
5
import 'package:cw_core/exceptions.dart';
6
import 'package:cw_core/transaction_direction.dart';
7
import 'package:cw_core/utils/print_verbose.dart';
8
+import 'package:cw_core/pathForWallet.dart';
9
+import 'package:cw_core/wallet_type.dart';
10
import 'package:cw_decred/amount_format.dart';
11
import 'package:cw_decred/pending_transaction.dart';
12
import 'package:cw_decred/transaction_credentials.dart';
@@ -307,9 +309,10 @@ abstract class DecredWalletBase
309
persistantPeer = addr;
310
await _libwallet.closeWallet(walletInfo.name);
311
final network = isTestnet ? "testnet" : "mainnet";
312
+ final dirPath = await pathForWalletDir(name: walletInfo.name, type: WalletType.decred);
313
final config = {
314
"name": walletInfo.name,
312
- "datadir": walletInfo.dirPath,
315
+ "datadir": dirPath,
316
"net": network,
317
"unsyncedaddrs": true,
318
};
@@ -605,22 +608,22 @@ abstract class DecredWalletBase
608
609
final sourceDir = Directory(currentDirPath);
610
final targetDir = Directory(newDirPath);
608
-
611
+
612
if (!targetDir.existsSync()) {
613
await targetDir.create(recursive: true);
614
}
612
-
615
+
616
await for (final entity in sourceDir.list(recursive: true)) {
614
- final relativePath = entity.path.substring(sourceDir.path.length+1);
617
+ final relativePath = entity.path.substring(sourceDir.path.length + 1);
618
final targetPath = p.join(targetDir.path, relativePath);
616
-
619
+
620
if (entity is File) {
621
await entity.rename(targetPath);
622
} else if (entity is Directory) {
623
await Directory(targetPath).create(recursive: true);
624
}
625
}
623
-
626
+
627
await sourceDir.delete(recursive: true);
628
}
629
cw_decred/lib/wallet_service.dart
+80
-19
@@ -8,6 +8,7 @@ import 'package:cw_core/wallet_service.dart';
8
import 'package:cw_core/pathForWallet.dart';
9
import 'package:cw_core/wallet_info.dart';
10
import 'package:cw_core/wallet_type.dart';
11
+import 'package:path/path.dart';
12
import 'package:hive/hive.dart';
13
import 'package:collection/collection.dart';
14
import 'package:cw_core/unspent_coins_info.dart';
@@ -57,42 +58,93 @@ class DecredWalletService extends WalletService<
58
@override
59
Future<DecredWallet> create(DecredNewWalletCredentials credentials, {bool? isTestnet}) async {
60
await this.init();
61
+ final dirPath = await pathForWalletDir(name: credentials.walletInfo!.name, type: getType());
62
+ final network = isTestnet == true ? testnet : mainnet;
63
final config = {
64
"name": credentials.walletInfo!.name,
62
- "datadir": credentials.walletInfo!.dirPath,
65
+ "datadir": dirPath,
66
"pass": credentials.password!,
64
- "net": isTestnet == true ? testnet : mainnet,
67
+ "net": network,
68
"unsyncedaddrs": true,
69
};
70
await libwallet!.createWallet(jsonEncode(config));
71
final di = DerivationInfo(
72
derivationPath: isTestnet == true ? seedRestorePathTestnet : seedRestorePath);
73
credentials.walletInfo!.derivationInfo = di;
74
+ credentials.walletInfo!.network = network;
75
+ // ios will move our wallet directory when updating. Since we must
76
+ // recalculate the new path every time we open the wallet, ensure this path
77
+ // is not used. An older wallet will have a directory here which is a
78
+ // condition for moving the wallet when opening, so this must be kept blank
79
+ // going forward.
80
+ credentials.walletInfo!.dirPath = "";
81
+ credentials.walletInfo!.path = "";
82
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
83
this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
84
await wallet.init();
85
return wallet;
86
}
87
88
+ void copyDirectorySync(Directory source, Directory destination) {
89
+ /// create destination folder if not exist
90
+ if (!destination.existsSync()) {
91
+ destination.createSync(recursive: true);
92
+ }
93
+
94
+ /// get all files from source (recursive: false is important here)
95
+ source.listSync(recursive: false).forEach((entity) {
96
+ final newPath = destination.path + Platform.pathSeparator + basename(entity.path);
97
+ if (entity is File) {
98
+ entity.rename(newPath);
99
+ } else if (entity is Directory) {
100
+ copyDirectorySync(entity, Directory(newPath));
101
+ }
102
+ });
103
+ }
104
+
105
+ Future<void> moveWallet(String fromPath, String toPath) async {
106
+ final oldWalletDir = new Directory(fromPath);
107
+ final newWalletDir = new Directory(toPath);
108
+ copyDirectorySync(oldWalletDir, newWalletDir);
109
+ // It would be ideal to delete the old directory here, but ios will error
110
+ // sometimes with "OS Error: No such file or directory, errno = 2" even
111
+ // after checking if it exists.
112
+ }
113
+
114
@override
115
Future<DecredWallet> openWallet(String name, String password) async {
116
final walletInfo = walletInfoSource.values
117
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
81
- final network = walletInfo.derivationInfo?.derivationPath == seedRestorePathTestnet ||
82
- walletInfo.derivationInfo?.derivationPath == pubkeyRestorePathTestnet
83
- ? testnet
84
- : mainnet;
118
+ if (walletInfo.network == null || walletInfo.network == "") {
119
+ walletInfo.network = walletInfo.derivationInfo?.derivationPath == seedRestorePathTestnet ||
120
+ walletInfo.derivationInfo?.derivationPath == pubkeyRestorePathTestnet
121
+ ? testnet
122
+ : mainnet;
123
+ }
124
125
await this.init();
87
- final walletDirExists = Directory(walletInfo.dirPath).existsSync();
88
- if (!walletDirExists) {
89
- walletInfo.dirPath = await pathForWalletDir(name: name, type: getType());
126
+
127
+ // Cake wallet version 4.27.0 and earlier gave a wallet dir that did not
128
+ // match the name. Move those to the correct place.
129
+ final dirPath = await pathForWalletDir(name: name, type: getType());
130
+ if (walletInfo.path != "") {
131
+ // On ios the stored dir no longer exists. We can only trust the basename.
132
+ // dirPath may already be updated and lost the basename, so look at path.
133
+ final randomBasename = basename(walletInfo.path);
134
+ final oldDir = await pathForWalletDir(name: randomBasename, type: getType());
135
+ if (oldDir != dirPath) {
136
+ await this.moveWallet(oldDir, dirPath);
137
+ }
138
+ // Clear the path so this does not trigger again.
139
+ walletInfo.dirPath = "";
140
+ walletInfo.path = "";
141
+ await walletInfo.save();
142
}
143
144
final config = {
93
- "name": walletInfo.name,
94
- "datadir": walletInfo.dirPath,
95
- "net": network,
145
+ "name": name,
146
+ "datadir": dirPath,
147
+ "net": walletInfo.network,
148
"unsyncedaddrs": true,
149
};
150
await libwallet!.loadWallet(jsonEncode(config));
@@ -127,12 +179,11 @@ class DecredWalletService extends WalletService<
179
180
await currentWallet.renameWalletFiles(newName);
181
130
- final newDirPath = await pathForWalletDir(name: newName, type: getType());
182
final newWalletInfo = currentWalletInfo;
183
newWalletInfo.id = WalletBase.idFor(newName, getType());
184
newWalletInfo.name = newName;
134
- newWalletInfo.dirPath = newDirPath;
135
- newWalletInfo.network = network;
185
+ newWalletInfo.dirPath = "";
186
+ newWalletInfo.path = "";
187
188
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
189
}
@@ -141,18 +192,23 @@ class DecredWalletService extends WalletService<
192
Future<DecredWallet> restoreFromSeed(DecredRestoreWalletFromSeedCredentials credentials,
193
{bool? isTestnet}) async {
194
await this.init();
195
+ final network = isTestnet == true ? testnet : mainnet;
196
+ final dirPath = await pathForWalletDir(name: credentials.walletInfo!.name, type: getType());
197
final config = {
198
"name": credentials.walletInfo!.name,
146
- "datadir": credentials.walletInfo!.dirPath,
199
+ "datadir": dirPath,
200
"pass": credentials.password!,
201
"mnemonic": credentials.mnemonic,
149
- "net": isTestnet == true ? testnet : mainnet,
202
+ "net": network,
203
"unsyncedaddrs": true,
204
};
205
await libwallet!.createWallet(jsonEncode(config));
206
final di = DerivationInfo(
207
derivationPath: isTestnet == true ? seedRestorePathTestnet : seedRestorePath);
208
credentials.walletInfo!.derivationInfo = di;
209
+ credentials.walletInfo!.network = network;
210
+ credentials.walletInfo!.dirPath = "";
211
+ credentials.walletInfo!.path = "";
212
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
213
this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
214
await wallet.init();
@@ -165,17 +221,22 @@ class DecredWalletService extends WalletService<
221
Future<DecredWallet> restoreFromKeys(DecredRestoreWalletFromPubkeyCredentials credentials,
222
{bool? isTestnet}) async {
223
await this.init();
224
+ final network = isTestnet == true ? testnet : mainnet;
225
+ final dirPath = await pathForWalletDir(name: credentials.walletInfo!.name, type: getType());
226
final config = {
227
"name": credentials.walletInfo!.name,
170
- "datadir": credentials.walletInfo!.dirPath,
228
+ "datadir": dirPath,
229
"pubkey": credentials.pubkey,
172
- "net": isTestnet == true ? testnet : mainnet,
230
+ "net": network,
231
"unsyncedaddrs": true,
232
};
233
await libwallet!.createWatchOnlyWallet(jsonEncode(config));
234
final di = DerivationInfo(
235
derivationPath: isTestnet == true ? pubkeyRestorePathTestnet : pubkeyRestorePath);
236
credentials.walletInfo!.derivationInfo = di;
237
+ credentials.walletInfo!.network = network;
238
+ credentials.walletInfo!.dirPath = "";
239
+ credentials.walletInfo!.path = "";
240
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
241
this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
242
await wallet.init();
scripts/android/build_decred.sh
+1
-1
@@ -7,7 +7,7 @@ cd "$(dirname "$0")"
7
CW_DECRED_DIR=$(realpath ../..)/cw_decred
8
LIBWALLET_PATH="${PWD}/decred/libwallet"
9
LIBWALLET_URL="https://github.com/decred/libwallet.git"
10
-LIBWALLET_VERSION="dba5327d35cb5d5d1ff113b780869deee154511f"
10
+LIBWALLET_VERSION="05f8d7374999400fe4d525eb365c39b77d307b14"
11
12
if [[ -e $LIBWALLET_PATH ]]; then
13
rm -fr $LIBWALLET_PATH || true
scripts/ios/build_decred.sh
+1
-1
@@ -3,7 +3,7 @@ set -e
3
. ./config.sh
4
LIBWALLET_PATH="${EXTERNAL_IOS_SOURCE_DIR}/libwallet"
5
LIBWALLET_URL="https://github.com/decred/libwallet.git"
6
-LIBWALLET_VERSION="dba5327d35cb5d5d1ff113b780869deee154511f"
6
+LIBWALLET_VERSION="05f8d7374999400fe4d525eb365c39b77d307b14"
7
8
if [[ -e $LIBWALLET_PATH ]]; then
9
rm -fr $LIBWALLET_PATH
scripts/macos/build_decred.sh
+1
-1
@@ -4,7 +4,7 @@
4
5
LIBWALLET_PATH="${EXTERNAL_MACOS_SOURCE_DIR}/libwallet"
6
LIBWALLET_URL="https://github.com/decred/libwallet.git"
7
-LIBWALLET_VERSION="dba5327d35cb5d5d1ff113b780869deee154511f"
7
+LIBWALLET_VERSION="05f8d7374999400fe4d525eb365c39b77d307b14"
8
9
echo "======================= DECRED LIBWALLET ========================="
10