What changed, and why it matters
This commit optimizes how the wallet prepares private Spark coin spends. It adds a check that throws an error if no spendable coins exist, and it changes the code to only fetch data for the specific coin groups the user actually owns, rather than scanning every group ID from 1 up to the latest one. This is a performance and robustness improvement, not a clear security fix, though it may reduce exposure to certain failure modes.
Review as a normal code-quality/performance change. No immediate security response is indicated, but verify the empty-coins exception is handled gracefully by callers to avoid crashes or poor user experience.
Security signals we found
Added input/state validation (empty spendable coin list)
Reduced scope of data fetched from cache/network to only owned coin groups
Performance optimization in privacy-coin spend path
Evidence from the diff
In spark_interface.dart, the Spark spend preparation logic is modified. First, an early guard is added: if coins (the list of spendable Spark coins) is empty, an exception is thrown immediately. Second, instead of iterating for (int i = 1; i <= currentId; i++) over all coin group IDs up to the latest global ID, the code now builds myCoinGroupIds from the user’s actual coins and iterates only those group IDs when calling FiroCacheCoordinator.getSetCoinsForGroupId. This reduces unnecessary cache lookups and network/database work.
Changed components
lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dartSpark coin spend preparationFiroCacheCoordinator integrationInspect captured patch +7 / −2
diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart
index f3729b2..0dec8aa 100644
--- a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart
+++ b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart
@@ -557,6 +557,10 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
.valueIntStringEqualTo("0")
.findAll();
+ if (coins.isEmpty) {
+ throw Exception("No spendable Spark coins found");
+ }
+
final available = info.cachedBalanceTertiary.spendable;
if (txAmount > available) {
@@ -577,10 +581,11 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
)
.toList();
- final currentId = await electrumXClient.getSparkLatestCoinId();
+ final myCoinGroupIds = coins.map((e) => e.groupId).toSet();
+
final List<Map<String, dynamic>> setMaps = [];
final List<({int groupId, String blockHash})> idAndBlockHashes = [];
- for (int i = 1; i <= currentId; i++) {
+ for (final i in myCoinGroupIds) {
final resultSet = await FiroCacheCoordinator.getSetCoinsForGroupId(
i,
network: cryptoCurrency.network,
Why this scored 28/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.