remove operator reward option from masternode registration
What changed, and why it matters
This commit removes the user-facing 'operator reward' field from the Firo masternode registration screen and hard-codes that value to 0. The change simplifies the form and prevents users from entering an operator reward percentage. There is no direct evidence in the commit that this fixes an active security vulnerability, but it does eliminate a potential source of user error or misuse in how masternodes are registered.
Treat as a hardening or feature-simplification change rather than a confirmed vulnerability fix. Review whether the operator reward parameter is still exposed through any other code path, RPC, or API surface. If the intent was to fix a security issue, request a CVE or vendor advisory for proper tracking.
Security signals we found
Removal of user-controlled parameter in a transaction-building path
Hard-coding of a previously configurable reward field to zero
Elimination of locale-dependent percentage parsing and rounding logic
No explicit security explanation in commit message or diff
Evidence from the diff
The patch deletes the operator reward input field and its validation/parsing logic from the masternode registration UI, and removes the operatorReward parameter from the Firo wallet’s registration method. The wallet now always writes nOperatorReward = 0 in the registration transaction payload. The original code parsed a percentage, converted it to basis points (0-10000), and validated the range. That path is now gone, so any issue tied to malformed or excessive operator rewards is no longer reachable through the UI.
Changed components
lib/pages/masternodes/sub_widgets/register_masternode_form.dartlib/wallets/wallet/impl/firo_wallet.dartInspect captured patch +1 / −62
### lib/pages/masternodes/sub_widgets/register_masternode_form.dart
@@ -1,12 +1,7 @@
-import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
-import '../../../providers/global/locale_provider.dart';
import '../../../providers/global/wallets_provider.dart';
-import '../../../utilities/amount/amount.dart';
-import '../../../utilities/amount/amount_field_relocalization.dart';
-import '../../../utilities/amount/amount_input_formatter.dart';
import '../../../themes/stack_colors.dart';
import '../../../utilities/if_not_already.dart';
import '../../../utilities/logger.dart';
@@ -48,7 +43,6 @@ class _RegisterMasternodeFormState
final _ipAndPortController = TextEditingController();
final _operatorPubKeyController = TextEditingController();
final _votingAddressController = TextEditingController();
- final _operatorRewardController = TextEditingController(text: "0");
final _payoutAddressController = TextEditingController();
TextStyle _getStyle(BuildContext context) {
@@ -65,17 +59,8 @@ class _RegisterMasternodeFormState
bool _enableCreateButton = false;
- // Parse as a 2-decimal Amount so pasted overprecision ("0.001") is
- // rejected instead of silently rounding to zero basis points.
- Decimal? get _operatorRewardPercent => Amount.tryParseEditableAmount(
- _operatorRewardController.text,
- locale: ref.read(localeServiceChangeNotifierProvider).locale,
- fractionDigits: 2,
- )?.decimal;
-
void _validate() {
if (mounted) {
- final percent = _operatorRewardPercent;
setState(() {
_enableCreateButton = [
_ipAndPortController.text
@@ -85,7 +70,6 @@ class _RegisterMasternodeFormState
.length ==
2,
_operatorPubKeyController.text.trim().isNotEmpty,
- percent != null && percent <= Decimal.fromInt(100),
_payoutAddressController.text.trim().isNotEmpty,
].every((e) => e);
});
@@ -100,18 +84,6 @@ class _RegisterMasternodeFormState
final votingAddress = _votingAddressController.text.trim();
final payoutAddress = _payoutAddressController.text.trim();
- // according to https://github.com/cypherstack/stack_wallet/blob/c898a70f808ed5490b8dd23571f5f162d9e38158/lib/wallets/wallet/impl/firo_wallet.dart#L1064
- // this should be a percent of 10000
- final operatorPercent = _operatorRewardPercent;
- if (operatorPercent == null) {
- throw Exception("Invalid operator reward");
- }
- final operatorReward = (operatorPercent * Decimal.fromInt(100))
- .round()
- .toBigInt()
- .toInt()
- .clamp(0, 10000);
-
final wallet =
ref.read(pWallets).getWallet(widget.firoWalletId) as FiroWallet;
@@ -120,7 +92,6 @@ class _RegisterMasternodeFormState
port,
operatorPubKey,
votingAddress,
- operatorReward,
payoutAddress,
collateralTxid: widget.collateralTxid,
collateralVout: widget.collateralVout,
@@ -177,7 +148,6 @@ class _RegisterMasternodeFormState
_ipAndPortController.dispose();
_operatorPubKeyController.dispose();
_votingAddressController.dispose();
- _operatorRewardController.dispose();
_payoutAddressController.dispose();
super.dispose();
}
@@ -186,12 +156,6 @@ class _RegisterMasternodeFormState
Widget build(BuildContext context) {
final stack = Theme.of(context).extension<StackColors>()!;
- listenForAmountRelocalization(
- ref.listen,
- controllers: [_operatorRewardController],
- onRelocalized: _validate,
- );
-
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
@@ -267,28 +231,6 @@ class _RegisterMasternodeFormState
),
SizedBox(height: Util.isDesktop ? 24 : 16),
- SelectableText("Operator reward (%)", style: _getStyle(context)),
- SizedBox(height: Util.isDesktop ? 10 : 8),
- AdaptiveTextField(
- controller: _operatorRewardController,
- showPasteClearButton: true,
- maxLines: 1,
- keyboardType: const TextInputType.numberWithOptions(decimal: true),
- inputFormatters: [
- AmountInputFormatter(
- controller: _operatorRewardController,
- decimals: 2,
- locale: ref.watch(
- localeServiceChangeNotifierProvider.select(
- (value) => value.locale,
- ),
- ),
- ),
- ],
- onChangedComprehensive: (_) => _validate(),
- ),
- SizedBox(height: Util.isDesktop ? 24 : 16),
-
SelectableText("Payout address", style: _getStyle(context)),
SizedBox(height: Util.isDesktop ? 10 : 8),
AdaptiveTextField(
### lib/wallets/wallet/impl/firo_wallet.dart
@@ -970,7 +970,6 @@ class FiroWallet<T extends ElectrumXCurrencyInterface> extends Bip39HDWallet<T>
int port,
String operatorPubKey,
String votingAddress,
- int operatorReward,
String payoutAddress, {
required String collateralTxid,
required int collateralVout,
@@ -1130,9 +1129,7 @@ class FiroWallet<T extends ElectrumXCurrencyInterface> extends Bip39HDWallet<T>
}
// nOperatorReward (16 bit)
- if (operatorReward < 0 || operatorReward > 10000) {
- throw Exception("Invalid operator reward: $operatorReward");
- }
+ const operatorReward = 0;
registrationTx.add(
(ByteData(
2,Why this scored 23/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.