pick owner address distinct from payout and voting addresses
What changed, and why it matters
This change fixes how Stack Wallet picks a special 'owner address' for Firo masternode-related operations. Previously, the wallet only made sure the owner address was different from the collateral address. Now it also ensures it differs from the payout and voting addresses. Using the same address for multiple roles could weaken privacy or cause operational problems in how the wallet tracks funds and masternode rewards.
Review related masternode address derivation logic to ensure collateral, payout, voting, and owner addresses are always mutually distinct. Consider adding tests covering address collision scenarios.
Security signals we found
Address reuse prevention across masternode roles
Firo masternode owner/payout/voting address separation
Privacy improvement by avoiding identical addresses for distinct transaction roles
Operational correctness fix for masternode address derivation
Evidence from the diff
In lib/wallets/wallet/impl/firo_wallet.dart, the code that derives an owner address for Firo masternodes was updated. A new helper, isUsableOwner, checks that the candidate address is non-null and not equal to collateralAddress, payoutAddress, or votingAddress. The loop and final validation now use this helper, and the error message was updated accordingly. This prevents address reuse across masternode roles.
Changed components
lib/wallets/wallet/impl/firo_wallet.dartFiro masternode owner address derivationInspect captured patch +10 / −8
### lib/wallets/wallet/impl/firo_wallet.dart
@@ -1020,20 +1020,22 @@ class FiroWallet<T extends ElectrumXCurrencyInterface> extends Bip39HDWallet<T>
);
}
+ bool isUsableOwner(Address? address) =>
+ address != null &&
+ address.value != collateralAddress &&
+ address.value != payoutAddress &&
+ address.value != votingAddress;
+
Address? ownerAddress = await getCurrentReceivingAddress();
const maxOwnerAttempts = 32;
- for (
- var i = 0;
- i < maxOwnerAttempts &&
- (ownerAddress == null || ownerAddress.value == collateralAddress);
- i++
- ) {
+ for (var i = 0; i < maxOwnerAttempts && !isUsableOwner(ownerAddress); i++) {
await generateNewReceivingAddress();
ownerAddress = await getCurrentReceivingAddress();
}
- if (ownerAddress == null || ownerAddress.value == collateralAddress) {
+ if (ownerAddress == null || !isUsableOwner(ownerAddress)) {
throw Exception(
- "Could not derive owner address distinct from collateral address.",
+ "Could not derive owner address distinct from collateral, payout "
+ "and voting addresses.",
);
}
Why this scored 59/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.