add option to ignore dust on private key sweep
What changed, and why it matters
This commit adds a user-facing checkbox labeled 'Ignore dust' to the private-key sweep feature in Sparrow Wallet. When enabled, very small ('dust') unspent outputs linked to the swept key are excluded from the transaction. This is a usability and privacy improvement, not a security fix, because dust outputs are often sent by third parties to track wallets or to make sweeps uneconomical due to fees. There is no evidence in the commit or supplied references that this addresses a vulnerability or was disclosed as a security issue.
No security action required. Treat as a normal feature/usability improvement. If reviewing for completeness, verify that getDustAttackThreshold() cannot be set to a negative or otherwise pathological value, and that the threshold is documented to users.
Security signals we found
Adds user-controlled filtering of UTXOs by value threshold
Uses existing 'dustAttackThreshold' configuration, implying prior anti-dust handling elsewhere
No input validation, cryptographic, or authorization changes
No mention of vulnerability, CVE, or security fix in commit message
Evidence from the diff
The change introduces an UnlabeledToggleSwitch named ignoreDust in PrivateKeySweepDialog and a removeDust() helper that filters TransactionOutput list against Config.get().getDustAttackThreshold(). If the toggle is selected, UTXOs with value less than or equal to the threshold are dropped before createTransaction() is called. If all UTXOs are filtered out, an error dialog is shown. The threshold is read from existing configuration, so no new policy logic is introduced. The diff is purely additive UI/flow code.
Changed components
src/main/java/com/sparrowwallet/sparrow/control/PrivateKeySweepDialog.javaInspect captured patch +21 / −2
diff --git a/src/main/java/com/sparrowwallet/sparrow/control/PrivateKeySweepDialog.java b/src/main/java/com/sparrowwallet/sparrow/control/PrivateKeySweepDialog.java
index 33f9fd8..43f30d6 100644
--- a/src/main/java/com/sparrowwallet/sparrow/control/PrivateKeySweepDialog.java
+++ b/src/main/java/com/sparrowwallet/sparrow/control/PrivateKeySweepDialog.java
@@ -68,6 +68,7 @@ public class PrivateKeySweepDialog extends Dialog<Transaction> {
private final ComboBox<Wallet> toWallet;
private final FeeRangeSlider feeRange;
private final CopyableLabel feeRate;
+ private final UnlabeledToggleSwitch ignoreDust;
private SilentPaymentAddress silentPaymentAddress;
public PrivateKeySweepDialog(Wallet wallet) {
@@ -170,7 +171,12 @@ public class PrivateKeySweepDialog extends Dialog<Transaction> {
feeRange.setFeeRate(AppServices.getDefaultFeeRate());
updateFeeRate();
- fieldset.getChildren().addAll(keyField, keyScriptTypeField, addressField, toAddressField, feeRangeField, feeRateField);
+ Field useDustLimitField = new Field();
+ useDustLimitField.setText("Ignore dust:");
+ ignoreDust = new UnlabeledToggleSwitch();
+ useDustLimitField.getInputs().add(ignoreDust);
+
+ fieldset.getChildren().addAll(keyField, keyScriptTypeField, addressField, toAddressField, feeRangeField, feeRateField, useDustLimitField);
form.getChildren().add(fieldset);
dialogPane.setContent(form);
@@ -383,7 +389,15 @@ public class PrivateKeySweepDialog extends Dialog<Transaction> {
ElectrumServer.AddressUtxosService addressUtxosService = new ElectrumServer.AddressUtxosService(fromAddress, since);
addressUtxosService.setOnSucceeded(successEvent -> {
- createTransaction(privateKey.getKey(), scriptType, addressUtxosService.getValue(), payment);
+ List<TransactionOutput> utxos = addressUtxosService.getValue();
+ if(ignoreDust.isSelected()) {
+ utxos = removeDust(utxos);
+ if(utxos.isEmpty()) {
+ AppServices.showErrorDialog("No outputs to sweep", "All of the unspent outputs for this private key have been ignored as dust.");
+ return;
+ }
+ }
+ createTransaction(privateKey.getKey(), scriptType, utxos, payment);
});
addressUtxosService.setOnFailed(failedEvent -> {
Throwable rootCause = Throwables.getRootCause(failedEvent.getSource().getException());
@@ -403,6 +417,11 @@ public class PrivateKeySweepDialog extends Dialog<Transaction> {
}
}
+ private List<TransactionOutput> removeDust(List<TransactionOutput> txOutputs) {
+ long dustAttackThreshold = Config.get().getDustAttackThreshold();
+ return txOutputs.stream().filter(txOutput -> txOutput.getValue() > dustAttackThreshold).collect(Collectors.toList());
+ }
+
private void createTransaction(ECKey privKey, ScriptType scriptType, List<TransactionOutput> txOutputs, Payment payment) {
Address destAddress = payment instanceof SilentPayment silentPayment ? computeSilentPaymentAddress(privKey, scriptType, txOutputs, silentPayment) : payment.getAddress();
ECKey pubKey = ECKey.fromPublicOnly(privKey);
Why this scored 19/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.