What changed, and why it matters
This commit fixes the way Spark Name registration data is embedded in blockchain transactions. Spark Names are human-readable aliases (like a username) tied to a privacy address. The previous code likely sent the registration fee to a plain address without attaching the required extra metadata, which could cause the name registration to fail or be recorded incorrectly. The patch adds the proper script format, including the name and Spark address, so the Firo network can recognize and process the registration. It also switches the underlying Spark mobile library from Cypher Stack's fork to the official Firoorg repository.
Review the prior implementation to confirm whether malformed registrations could result in loss of registration fees, name squatting, or denial of name service. Validate that the new script format matches the Firo Spark Name protocol specification. Audit the upstream flutter_libsparkmobile changes at the new ref for any related security fixes.
Security signals we found
Transaction output script format change for Spark Name registration
Addition of OP_SPARKNAMEID and OP_DROP opcodes to embed name and Spark address metadata
Dependency source switched from Cypher Stack fork to upstream Firoorg repository
Potential prior incorrect registration format could lead to failed or misattributed name registrations
Evidence from the diff
The diff modifies SparkInterface to construct a Spark Name registration output script. Previously, the output script was generated solely from the recipient address via bitcoin-dart’s addressToOutputScript. The patch introduces _sparkNameFeeScript, which appends a script fragment containing OP_SPARKNAMEID (0xe1), the UTF-8 encoded name, OP_DROP (0x75), the UTF-8 encoded Spark address, and another OP_DROP. This appended script is conditionally added when txData.sparkNameInfo is present. Additionally, the pubspec.lock and template now point flutter_libsparkmobile to firoorg/flutter_libsparkmobile at ref 171bc186663e3c7a573a6240f28f430e8d6b7d50 instead of cypherstack/flutter_libsparkmobile at 4bd84c88e1b2a817a2604ec53030634cc3304bc7.
Changed components
lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dartpubspec.lockscripts/app_config/templates/pubspec.template.yamlSpark Name registration transaction constructionflutter_libsparkmobile dependencyInspect captured patch +31 / −6
diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart
index fcf753f..eaf82f4 100644
--- a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart
+++ b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart
@@ -4,6 +4,7 @@ import 'dart:isolate';
import 'dart:math';
import 'package:bitcoindart/bitcoindart.dart' as btc;
+import 'package:bitcoindart/src/utils/script.dart' as bscript;
import 'package:coinlib_flutter/coinlib_flutter.dart' as coinlib;
import 'package:decimal/decimal.dart';
import 'package:flutter/foundation.dart';
@@ -50,6 +51,8 @@ const SPARK_OUT_LIMIT_PER_TX = 16;
const OP_SPARKMINT = 0xd1;
const OP_SPARKSMINT = 0xd2;
const OP_SPARKSPEND = 0xd3;
+const OP_SPARKNAMEID = 0xe1;
+const OP_DROP = 0x75;
/// top level function for use with [compute]
String _hashTag(String tag) {
@@ -61,6 +64,21 @@ String _hashTag(String tag) {
return hash;
}
+Uint8List _sparkNameFeeScript({
+ required Uint8List baseScript,
+ required String name,
+ required String sparkAddress,
+}) => Uint8List.fromList([
+ ...baseScript,
+ ...bscript.compile([
+ OP_SPARKNAMEID,
+ Uint8List.fromList(utf8.encode(name)),
+ OP_DROP,
+ Uint8List.fromList(utf8.encode(sparkAddress)),
+ OP_DROP,
+ ]),
+]);
+
void initSparkLogging(Level level) => libSpark.initSparkLogging(level);
abstract class _SparkIsolate {
@@ -708,10 +726,17 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
),
);
- final scriptPubKey = btc.Address.addressToOutputScript(
+ var scriptPubKey = btc.Address.addressToOutputScript(
txData.recipients![i].address,
_bitcoinDartNetwork,
);
+ if (txData.sparkNameInfo != null) {
+ scriptPubKey = _sparkNameFeeScript(
+ baseScript: scriptPubKey,
+ name: txData.sparkNameInfo!.name,
+ sparkAddress: txData.sparkNameInfo!.sparkAddress.value,
+ );
+ }
txb.addOutput(
scriptPubKey,
recipientsWithFeeSubtracted[i].amount.raw.toInt(),
diff --git a/pubspec.lock b/pubspec.lock
index 091d58a..1baec63 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -1028,9 +1028,9 @@ packages:
dependency: "direct main"
description:
path: "."
- ref: "4bd84c88e1b2a817a2604ec53030634cc3304bc7"
- resolved-ref: "4bd84c88e1b2a817a2604ec53030634cc3304bc7"
- url: "https://github.com/cypherstack/flutter_libsparkmobile.git"
+ ref: "171bc186663e3c7a573a6240f28f430e8d6b7d50"
+ resolved-ref: "171bc186663e3c7a573a6240f28f430e8d6b7d50"
+ url: "https://github.com/firoorg/flutter_libsparkmobile.git"
source: git
version: "0.1.0"
flutter_lints:
diff --git a/scripts/app_config/templates/pubspec.template.yaml b/scripts/app_config/templates/pubspec.template.yaml
index 6764428..7ebc0b2 100644
--- a/scripts/app_config/templates/pubspec.template.yaml
+++ b/scripts/app_config/templates/pubspec.template.yaml
@@ -43,8 +43,8 @@ dependencies:
# %%ENABLE_FIRO%%
# flutter_libsparkmobile:
# git:
-# url: https://github.com/cypherstack/flutter_libsparkmobile.git
-# ref: 4bd84c88e1b2a817a2604ec53030634cc3304bc7
+# url: https://github.com/firoorg/flutter_libsparkmobile.git
+# ref: 171bc186663e3c7a573a6240f28f430e8d6b7d50
# %%END_ENABLE_FIRO%%
# %%ENABLE_EPIC%%
Why this scored 31/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.