fetch params from download.z.cash on demand to reduce app size (#2879)
add restore height to seed screen
cyan committed
Feb 10, 2026 at 00:44 UTC
6b458e8a3b3e42af75280ddfd4f5ce7a56d6fb2d
4 files changed
+52
-6
cw_zcash/lib/src/zcash_taddress_rotation.dart
+1
@@ -313,6 +313,7 @@ class ZcashTaddressRotation {
313
314
final recipient = Recipient(recipientBuilder.toBytes());
315
final fee = FeeT(fee: 10000, minFee: 0, maxFee: 0, scheme: 0);
316
+ await ZcashWalletBase.loadProver();
317
final txPlan = await ZcashWalletService.runInDbMutex(
318
() => WarpApi.prepareTx(
319
coin,
cw_zcash/lib/src/zcash_wallet.dart
+44
-4
@@ -11,6 +11,7 @@ import 'package:cw_core/sync_status.dart';
11
import 'package:cw_core/transaction_direction.dart';
12
import 'package:cw_core/transaction_priority.dart';
13
import 'package:cw_core/utils/print_verbose.dart';
14
+import 'package:cw_core/utils/proxy_wrapper.dart';
15
import 'package:cw_core/wallet_addresses.dart';
16
import 'package:cw_core/wallet_base.dart';
17
import 'package:cw_core/wallet_credentials.dart';
@@ -220,6 +221,7 @@ abstract class ZcashWalletBase
221
222
// pools parameter: bitmask for which pools to use for sending
223
// 1=Transparent, 2=Sapling, 4=Orchard, 7=All pools
224
+ await ZcashWalletBase.loadProver();
225
// Using 7 (all pools) allows spending from any pool type
226
final txPlan = await ZcashWalletService.runInDbMutex(
227
() => WarpApi.prepareTx(
@@ -333,6 +335,8 @@ abstract class ZcashWalletBase
335
"privateViewKey": backup.fvk,
336
"uvk": backup.uvk,
337
"tsk": backup.tsk,
338
+ if (lastKnownRestoreHeight != null)
339
+ "restoreHeight": lastKnownRestoreHeight.toString(),
340
};
341
}
342
@@ -354,12 +358,14 @@ abstract class ZcashWalletBase
358
bool get hasRescan => true;
359
360
static Future<void> storeZcashHeight(final int height) async {
361
+ lastKnownRestoreHeight = height;
362
final zcashDir = await pathForWalletTypeDir(type: WalletType.zcash);
363
final zcashInitialSync = File(p.join(zcashDir, ".initial-sync-marker"));
364
zcashInitialSync.writeAsBytesSync([0x00]);
365
zcashInitialSync.writeAsStringSync(height.toString(), mode: FileMode.writeOnlyAppend);
366
}
367
368
+ static int? lastKnownRestoreHeight = null;
369
static Future<int?> loadZcashHeight() async {
370
final zcashDir = await pathForWalletTypeDir(type: WalletType.zcash);
371
final zcashInitialSync = File(p.join(zcashDir, ".initial-sync-marker"));
@@ -371,7 +377,8 @@ abstract class ZcashWalletBase
377
return null;
378
}
379
final heightString = String.fromCharCodes(bytes.skip(1));
374
- return int.tryParse(heightString);
380
+ lastKnownRestoreHeight = int.tryParse(heightString);
381
+ return lastKnownRestoreHeight;
382
}
383
384
static int zashiAnnouncedBlockHeight = 2419420;
@@ -749,6 +756,7 @@ abstract class ZcashWalletBase
756
757
final recipient = Recipient(recipientBuilder.toBytes());
758
final fee = FeeT(fee: 10000, minFee: 0, maxFee: 0, scheme: 0);
759
+ await ZcashWalletBase.loadProver();
760
final txPlan = await ZcashWalletService.runInDbMutex(
761
() => WarpApi.prepareTx(
762
coin,
@@ -1039,13 +1047,45 @@ abstract class ZcashWalletBase
1047
} catch (e) {
1048
printV("zec init failed: $e");
1049
} // do not fail on network exception
1042
- final spend = await rootBundle.load('scripts/zcash_lib/assets/sapling-spend.params');
1043
- final output = await rootBundle.load('scripts/zcash_lib/assets/sapling-output.params');
1044
- WarpApi.initProver(spend.buffer.asUint8List(), output.buffer.asUint8List());
1050
+ await loadZcashHeight();
1051
+
1052
+ unawaited(loadProver());
1053
+
1054
await ZcashTaddressRotation.init();
1055
await ZcashTransactionInfo.init();
1056
_initialized = true;
1057
}
1058
+
1059
+ static bool isProverLoaded = false;
1060
+ static Future<void> loadProver() async {
1061
+ Uint8List? spend;
1062
+ Uint8List? output;
1063
+ final cacheDir = await getApplicationCacheDirectory();
1064
+ try {
1065
+ final spendBundle = await rootBundle.load('scripts/zcash_lib/assets/sapling-spend.params');
1066
+ final outputBundle = await rootBundle.load('scripts/zcash_lib/assets/sapling-output.params');
1067
+ spend = spendBundle.buffer.asUint8List();
1068
+ output = outputBundle.buffer.asUint8List();
1069
+ if (spend.length == 0 || output.length == 0) throw Exception("NUH UH");
1070
+ spend = await File(cacheDir.path+"/sapling-spend.params").readAsBytesSync();
1071
+ output = await File(cacheDir.path+"/sapling-output.params").readAsBytesSync();
1072
+ if (spend.length == 0 || output.length == 0) throw Exception("NUH UH");
1073
+ } catch (e) {
1074
+ printV("$e. Fine, I'll download them.");
1075
+ final spendResponse = await ProxyWrapper().get(
1076
+ clearnetUri: Uri.parse("https://download.z.cash/downloads/sapling-spend.params"),
1077
+ );
1078
+ final outputResponse = await ProxyWrapper().get(
1079
+ clearnetUri: Uri.parse("https://download.z.cash/downloads/sapling-output.params"),
1080
+ );
1081
+ spend = spendResponse.bodyBytes;
1082
+ output = outputResponse.bodyBytes;
1083
+ await File(cacheDir.path+"/sapling-spend.params").writeAsBytes(spend);
1084
+ await File(cacheDir.path+"/sapling-output.params").writeAsBytes(output);
1085
+ }
1086
+ WarpApi.initProver(spend, output);
1087
+ isProverLoaded = true;
1088
+ }
1089
1090
static Future<int> getBlockHeightByTime(final DateTime time) async {
1091
final genesisTime = DateTime.utc(2016, 10, 28);
lib/src/screens/wallet_keys/wallet_keys_page.dart
+1
-1
@@ -183,7 +183,7 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
183
Widget _buildSeedTab(BuildContext context, bool isLegacySeed) {
184
return Column(
185
children: [
186
- if (isLegacySeedOnly || isLegacySeed || widget.walletKeysViewModel.isBitcoin) ...[
186
+ if (isLegacySeedOnly || isLegacySeed ||widget.walletKeysViewModel.shouldShowHeightBox) ...[
187
_buildHeightBox(),
188
const SizedBox(height: 20),
189
],
lib/view_model/wallet_keys_view_model.dart
+6
-1
@@ -62,7 +62,9 @@ abstract class WalletKeysViewModelBase with Store {
62
}
63
64
bool get isBitcoin => _wallet.type == WalletType.bitcoin;
65
-
65
+
66
+ // this is incomplete, needs legacy seed toggle for XMR
67
+ bool get shouldShowHeightBox => [WalletType.bitcoin, WalletType.zcash].contains(_wallet.type);
68
final ObservableList<StandartListItem> items;
69
70
@observable
@@ -318,6 +320,9 @@ abstract class WalletKeysViewModelBase with Store {
320
if (_wallet.type == WalletType.wownero) {
321
return wownero!.getRestoreHeight(_wallet)?.toString();
322
}
323
+ if (_wallet.type == WalletType.zcash) {
324
+ return zcash!.getKeys(_wallet)["restoreHeight"]?.toString();
325
+ }
326
if (_restoreHeightByTransactions != 0)
327
return getRoundedRestoreHeight(_restoreHeightByTransactions);
328
if (_restoreHeight != 0) return _restoreHeight.toString();