re add try catch in init() (and dart format)
What changed, and why it matters
This commit re-adds a try/catch block around wallet initialization code for the Spark privacy feature. It means if something goes wrong while setting up Spark addresses or keys, the app will log the error but still let the user open the wallet, rather than crashing. Most of the change is automatic code formatting. The security relevance is unclear from the commit alone: it could be a stability fix, or it could mask a deeper problem that previously caused crashes.
Treat as a low-confidence signal requiring follow-up. Review why the try/catch was removed and re-added, whether any specific exception triggered the change, and whether silently continuing past init failures could leave the wallet in an inconsistent state (e.g., missing Spark address, stale cache, or incorrect view key). Ask the project maintainers for context or a linked issue.
Security signals we found
Broad exception swallowing in wallet initialization path
Removal of fatal error behavior for Spark address/key setup failures
Possible masking of cryptographic or state-integrity failures
No explicit security context, CVE, or advisory referenced in commit
Evidence from the diff
The diff wraps the body of SparkInterface.init() in a try/catch that catches all exceptions, logs them, and then proceeds to call super.init(). Previously, exceptions during Spark setup (e.g., address derivation failures, view-key loading errors, or cache reset failures) would propagate and likely abort wallet initialization. The rest of the diff is dart format reformatting plus removal of an unused import (view_only_wallet_data.dart). There is no explicit security claim in the commit message or diff.
Changed components
lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dartSpark wallet initialization (init())Firo/Spark address and view-key derivationInspect captured patch +97 / −79
diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart
index 03443fc..b5c95ab 100644
--- a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart
+++ b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart
@@ -34,7 +34,6 @@ import '../../models/tx_data.dart';
import '../intermediate/bip39_hd_wallet.dart';
import 'cpfp_interface.dart';
import 'electrumx_interface.dart';
-import '../../../models/keys/view_only_wallet_data.dart';
const kDefaultSparkIndex = 1;
@@ -105,8 +104,8 @@ Future<R> computeWithLibSparkLogging<M, R>(
return _SparkIsolate.run(callback, message);
}
-
-mixin SparkInterface<T extends ElectrumXCurrencyInterface> on Bip39HDWallet<T>, ElectrumXInterface<T> {
+mixin SparkInterface<T extends ElectrumXCurrencyInterface>
+ on Bip39HDWallet<T>, ElectrumXInterface<T> {
late Address _currentSparkAddress;
late String viewKeyHex;
@@ -152,7 +151,10 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface> on Bip39HDWallet<T>,
);
}
- static bool validateSparkAddress({required String address, required bool isTestNet}) {
+ static bool validateSparkAddress({
+ required String address,
+ required bool isTestNet,
+ }) {
return libSpark.validateAddress(address: address, isTestNet: isTestNet);
}
@@ -160,32 +162,35 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface> on Bip39HDWallet<T>,
required List<dynamic> anonymitySetCoins,
required int groupId,
}) async {
- return await computeWithLibSparkLogging(
- identifyCoinsStatic,
- (
- walletId_: walletId,
- viewKeyHex_: viewKeyHex,
- isTestNet_: isTestNet,
- anonymitySetCoins: anonymitySetCoins,
- groupId: groupId,
- ),
- );
+ return await computeWithLibSparkLogging(identifyCoinsStatic, (
+ walletId_: walletId,
+ viewKeyHex_: viewKeyHex,
+ isTestNet_: isTestNet,
+ anonymitySetCoins: anonymitySetCoins,
+ groupId: groupId,
+ ));
}
- static Future<List<SparkCoin>> identifyCoinsStatic(({
- List<dynamic> anonymitySetCoins,
- int groupId,
- bool isTestNet_,
- String viewKeyHex_,
- String walletId_,
- }) args) async {
+ static Future<List<SparkCoin>> identifyCoinsStatic(
+ ({
+ List<dynamic> anonymitySetCoins,
+ int groupId,
+ bool isTestNet_,
+ String viewKeyHex_,
+ String walletId_,
+ })
+ args,
+ ) async {
final List<SparkCoin> myCoins = [];
for (final dynData in args.anonymitySetCoins) {
final data = List<String>.from(dynData as List);
if (data.length != 3) {
- Logging.instance.e("Unexpected serialized coin info found", error: data);
+ Logging.instance.e(
+ "Unexpected serialized coin info found",
+ error: data,
+ );
continue;
}
@@ -215,7 +220,10 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface> on Bip39HDWallet<T>,
case 1:
coinType = SparkCoinType.spend;
default:
- Logging.instance.e("Unknown spark coin type detected", error: coin.type.value);
+ Logging.instance.e(
+ "Unknown spark coin type detected",
+ error: coin.type.value,
+ );
continue;
}
@@ -258,62 +266,71 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface> on Bip39HDWallet<T>,
@override
Future<void> init() async {
- final sparkUsedTagsResetVersion =
- info.otherData[WalletInfoKeys.firoSparkUsedTagsCacheResetVersion]
- as int? ??
- 0;
+ try {
+ final sparkUsedTagsResetVersion =
+ info.otherData[WalletInfoKeys.firoSparkUsedTagsCacheResetVersion]
+ as int? ??
+ 0;
+
+ if (sparkUsedTagsResetVersion == 0) {
+ await info.updateOtherData(
+ newEntries: {WalletInfoKeys.firoSparkUsedTagsCacheResetVersion: 1},
+ isar: mainDB.isar,
+ );
+ await FiroCacheCoordinator.clearSharedCache(
+ cryptoCurrency.network,
+ clearOnlyUsedTagsCache: true,
+ );
+ }
- if (sparkUsedTagsResetVersion == 0) {
- await info.updateOtherData(
- newEntries: {WalletInfoKeys.firoSparkUsedTagsCacheResetVersion: 1},
- isar: mainDB.isar,
- );
- await FiroCacheCoordinator.clearSharedCache(
- cryptoCurrency.network,
- clearOnlyUsedTagsCache: true,
- );
- }
+ if (isViewOnly) {
+ final walletData =
+ await getViewOnlyWalletData() as SparkViewOnlyWalletData;
+ viewKeyHex = walletData.viewKey;
+ } else {
+ final root = await getRootHDNode();
+ final privateKey = root.derivePath(sparkDerivationPath).privateKey.data;
+ viewKeyHex = libSpark.getFullViewKeyHexFromPrivateKeyData(
+ privateKeyHex: privateKey.toHex,
+ index: sparkIndex,
+ );
+ }
- if (isViewOnly) {
- final walletData = await getViewOnlyWalletData() as SparkViewOnlyWalletData;
- viewKeyHex = walletData.viewKey;
- } else {
- final root = await getRootHDNode();
- final privateKey = root.derivePath(sparkDerivationPath).privateKey.data;
- viewKeyHex = libSpark.getFullViewKeyHexFromPrivateKeyData(privateKeyHex: privateKey.toHex, index: sparkIndex);
- }
+ Address? address = await getCurrentReceivingSparkAddress();
+ if (address == null) {
+ address = await generateSparkAddress(1);
+ await mainDB.putAddress(address);
+ }
- Address? address = await getCurrentReceivingSparkAddress();
- if (address == null) {
- address = await generateSparkAddress(1);
- await mainDB.putAddress(address);
- }
+ if (address.derivationIndex == -1) {
+ throw Exception("Error finding spark receiving address");
+ }
- if (address.derivationIndex == -1) {
- throw Exception("Error finding spark receiving address");
+ _currentSparkAddress = address;
+ sparkChangeAddress = await generateSparkAddress(libSpark.sparkChange);
+ } catch (e, s) {
+ // do nothing, still allow user into wallet
+ Logging.instance.e("$runtimeType init() failed", error: e, stackTrace: s);
}
- _currentSparkAddress = address;
- sparkChangeAddress = await generateSparkAddress(libSpark.sparkChange);
-
await super.init();
}
@override
Future<List<Address>> fetchAddressesForElectrumXScan() async {
return await mainDB
- .getAddresses(walletId)
- .filter()
- .not()
- .group(
- (q) => q
- .typeEqualTo(AddressType.spark)
- .or()
- .typeEqualTo(AddressType.nonWallet)
- .or()
- .subTypeEqualTo(AddressSubType.nonWallet),
- )
- .findAll();
+ .getAddresses(walletId)
+ .filter()
+ .not()
+ .group(
+ (q) => q
+ .typeEqualTo(AddressType.spark)
+ .or()
+ .typeEqualTo(AddressType.nonWallet)
+ .or()
+ .subTypeEqualTo(AddressSubType.nonWallet),
+ )
+ .findAll();
}
Future<Address?> getCurrentReceivingSparkAddress() async {
@@ -322,17 +339,19 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface> on Bip39HDWallet<T>,
return _currentSparkAddress;
} catch (e) {
return await mainDB.isar.addresses
- .where()
- .walletIdEqualTo(walletId)
- .filter()
- .typeEqualTo(AddressType.spark)
- .sortByDerivationIndexDesc()
- .findFirst();
+ .where()
+ .walletIdEqualTo(walletId)
+ .filter()
+ .typeEqualTo(AddressType.spark)
+ .sortByDerivationIndexDesc()
+ .findFirst();
}
}
Future<Address> generateNextSparkAddress() async {
- final newAddress = await generateSparkAddress(_currentSparkAddress.derivationIndex + 1);
+ final newAddress = await generateSparkAddress(
+ _currentSparkAddress.derivationIndex + 1,
+ );
_currentSparkAddress = newAddress;
return newAddress;
}
@@ -596,7 +615,8 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface> on Bip39HDWallet<T>,
fractionDigits: cryptoCurrency.fractionDigits,
),
memo: txData.sparkRecipients![i].memo,
- isChange: sparkChangeAddress.value == txData.sparkRecipients![i].address,
+ isChange:
+ sparkChangeAddress.value == txData.sparkRecipients![i].address,
));
}
@@ -953,7 +973,6 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface> on Bip39HDWallet<T>,
groupId: groupId,
);
-
// add checked txids after identification
_mempoolTxidsChecked.addAll(checkedTxids);
@@ -1118,9 +1137,8 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface> on Bip39HDWallet<T>,
}
// check for spark coins in mempool
- final List<SparkCoin> mempoolMyCoins = await _refreshSparkCoinsMempoolCheck(
- groupId: latestGroupId,
- );
+ final List<SparkCoin> mempoolMyCoins =
+ await _refreshSparkCoinsMempoolCheck(groupId: latestGroupId);
// if any were found, add to database
if (mempoolMyCoins.isNotEmpty) {
Why this scored 34/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.