What changed, and why it matters
This commit fixes a logic bug in how the Zcash wallet loads cryptographic 'proving' files. Previously, the app would load the files from app assets, then immediately overwrite those loaded bytes by reading from a local cache—even if the asset load had succeeded. After the fix, the app only falls back to the cache when the asset load produced empty data. This prevents silently using stale or corrupted cached parameters and avoids unnecessary network downloads.
Review whether cached/downloaded proving parameters should be verified against known hashes or signatures before use, and confirm the fallback order (bundled asset → cache → network) matches intended trust assumptions. Consider adding tests covering each branch of loadProver().
Security signals we found
Logic error: unconditional overwrite of freshly loaded asset bytes with cache-file bytes
Potential use of stale or tampered cached cryptographic parameters (sapling-spend.params / sapling-output.params)
Redundant network fallback triggered because cache read could zero out valid asset data
No input validation or integrity check (hash/signature) on downloaded or cached parameter files
Evidence from the diff
In cw_zcash/lib/src/zcash_wallet.dart, the loadProver() method loads sapling-spend.params and sapling-output.params from Flutter rootBundle assets, checks their length, and then unconditionally re-reads them from cacheDir files. The patch moves the cache-file read inside the empty-length guard so it is only executed when the asset bundle returned zero-byte buffers. This corrects the control flow so successful asset loads are preserved, while still providing a fallback for the empty-asset case. Minor formatting changes (trailing whitespace, string concatenation spacing, single-line if formatting) are also included.
Changed components
cw_zcash/lib/src/zcash_wallet.dartZcashWalletBase.loadProver()Sapling spend/output parameter loadingInspect captured patch +8 / −8
diff --git a/cw_zcash/lib/src/zcash_wallet.dart b/cw_zcash/lib/src/zcash_wallet.dart
index bfce46d4..e9a44adb 100644
--- a/cw_zcash/lib/src/zcash_wallet.dart
+++ b/cw_zcash/lib/src/zcash_wallet.dart
@@ -335,8 +335,7 @@ abstract class ZcashWalletBase
"privateViewKey": backup.fvk,
"uvk": backup.uvk,
"tsk": backup.tsk,
- if (lastKnownRestoreHeight != null)
- "restoreHeight": lastKnownRestoreHeight.toString(),
+ if (lastKnownRestoreHeight != null) "restoreHeight": lastKnownRestoreHeight.toString(),
};
}
@@ -1055,7 +1054,7 @@ abstract class ZcashWalletBase
await ZcashTransactionInfo.init();
_initialized = true;
}
-
+
static bool isProverLoaded = false;
static Future<void> loadProver() async {
Uint8List? spend;
@@ -1066,9 +1065,10 @@ abstract class ZcashWalletBase
final outputBundle = await rootBundle.load('scripts/zcash_lib/assets/sapling-output.params');
spend = spendBundle.buffer.asUint8List();
output = outputBundle.buffer.asUint8List();
- if (spend.length == 0 || output.length == 0) throw Exception("NUH UH");
- spend = await File(cacheDir.path+"/sapling-spend.params").readAsBytesSync();
- output = await File(cacheDir.path+"/sapling-output.params").readAsBytesSync();
+ if (spend.length == 0 || output.length == 0) {
+ spend = await File(cacheDir.path + "/sapling-spend.params").readAsBytesSync();
+ output = await File(cacheDir.path + "/sapling-output.params").readAsBytesSync();
+ }
if (spend.length == 0 || output.length == 0) throw Exception("NUH UH");
} catch (e) {
printV("$e. Fine, I'll download them.");
@@ -1080,8 +1080,8 @@ abstract class ZcashWalletBase
);
spend = spendResponse.bodyBytes;
output = outputResponse.bodyBytes;
- await File(cacheDir.path+"/sapling-spend.params").writeAsBytes(spend);
- await File(cacheDir.path+"/sapling-output.params").writeAsBytes(output);
+ await File(cacheDir.path + "/sapling-spend.params").writeAsBytes(spend);
+ await File(cacheDir.path + "/sapling-output.params").writeAsBytes(output);
}
WarpApi.initProver(spend, output);
isProverLoaded = true;
Why this scored 42/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.