Fix IP and port serialization in Firo masternode transactions.
What changed, and why it matters
This commit fixes how IP addresses and port numbers are packed into Firo masternode registration transactions in the Stack Wallet mobile/desktop wallet. The previous code reversed the IP octets and used little-endian byte order for the port, which would produce an incorrectly formatted network message. The patch now keeps IP octets in normal order and writes the port in network (big-endian) byte order, matching standard Firo protocol expectations. It also rejects port 0 as invalid. A wallet using the old code would likely create masternode transactions that other Firo nodes could not correctly interpret, leading to registration failures or misdirected masternode announcements.
Treat this as a correctness bug with possible availability/reliability impact for Firo masternode operators using Stack Wallet. Review whether any released versions shipped with the reversed serialization and, if so, consider a patch release. No independent exploit path is evident from the diff alone, but users relying on masternode registration should update and re-test registration flow.
Security signals we found
Serialization byte-order bug in blockchain transaction construction
Incorrect network byte order for IP and port fields
Potential transaction malleability or invalid masternode registration
Port 0 now rejected, reducing malformed output
Evidence from the diff
In lib/wallets/wallet/impl/firo_wallet.dart the masternode registration serialization was changed in three ways: (1) the IPv4 octet list is no longer reversed before being appended, (2) the 2-byte port is now serialized with Endian.big instead of Endian.little, and (3) the valid port range was tightened from 0..65535 to 1..65535. The original code’s comment claimed ‘network byte order’ but then reversed the IP and used little-endian for the port, so the comment and behavior were inconsistent. The corrected serialization aligns with conventional network byte order (big-endian for multi-byte fields, octets in natural order).
Changed components
lib/wallets/wallet/impl/firo_wallet.dartFiro masternode registration transaction builderInspect captured patch +4 / −5
diff --git a/lib/wallets/wallet/impl/firo_wallet.dart b/lib/wallets/wallet/impl/firo_wallet.dart
index bbb9f10..c3b861f 100644
--- a/lib/wallets/wallet/impl/firo_wallet.dart
+++ b/lib/wallets/wallet/impl/firo_wallet.dart
@@ -999,9 +999,7 @@ class FiroWallet<T extends ElectrumXCurrencyInterface> extends Bip39HDWallet<T>
final ipParts = ip
.split('.')
.map((e) => int.parse(e))
- .toList()
- .reversed
- .toList(); // network byte order
+ .toList();
if (ipParts.length != 4) {
throw Exception("Invalid IP address: $ip");
}
@@ -1017,11 +1015,12 @@ class FiroWallet<T extends ElectrumXCurrencyInterface> extends Bip39HDWallet<T>
registrationTx.add(ipParts);
// addr.port (2 bytes)
- if (port < 0 || port > 65535) {
+ if (port < 1 || port > 65535) {
throw Exception("Invalid port: $port");
}
registrationTx.add(
- (ByteData(2)..setInt16(0, port, Endian.little)).buffer.asUint8List(),
+ // network byte order
+ (ByteData(2)..setInt16(0, port, Endian.big)).buffer.asUint8List(),
);
// keyIDOwner (20 bytes)
Why this scored 56/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.