mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-23 20:36:44 +00:00
add dust attack warning to utxos tab where small value txes are received on used addresses
This commit is contained in:
parent
674498052f
commit
c0ca74ce6a
5 changed files with 55 additions and 6 deletions
|
@ -37,11 +37,13 @@ public class AddressCell extends TreeTableCell<Entry, UtxoEntry.AddressStatus> {
|
|||
setContextMenu(new EntryCell.AddressContextMenu(address, utxoEntry.getOutputDescriptor(), new NodeEntry(utxoEntry.getWallet(), utxoEntry.getNode())));
|
||||
Tooltip tooltip = new Tooltip();
|
||||
tooltip.setShowDelay(Duration.millis(250));
|
||||
tooltip.setText(getTooltipText(utxoEntry, addressStatus.isDuplicate()));
|
||||
tooltip.setText(getTooltipText(utxoEntry, addressStatus.isDuplicate(), addressStatus.isDustAttack()));
|
||||
setTooltip(tooltip);
|
||||
|
||||
if(addressStatus.isDuplicate()) {
|
||||
setGraphic(getDuplicateGlyph());
|
||||
} else if(addressStatus.isDustAttack()) {
|
||||
setGraphic(getDustAttackGlyph());
|
||||
} else {
|
||||
setGraphic(null);
|
||||
}
|
||||
|
@ -49,9 +51,9 @@ public class AddressCell extends TreeTableCell<Entry, UtxoEntry.AddressStatus> {
|
|||
}
|
||||
}
|
||||
|
||||
private String getTooltipText(UtxoEntry utxoEntry, boolean duplicate) {
|
||||
private String getTooltipText(UtxoEntry utxoEntry, boolean duplicate, boolean dustAttack) {
|
||||
return (utxoEntry.getNode().getWallet().isNested() ? utxoEntry.getNode().getWallet().getDisplayName() + " " : "" ) +
|
||||
utxoEntry.getNode().toString() + (duplicate ? " (Duplicate address)" : "");
|
||||
utxoEntry.getNode().toString() + (duplicate ? " (Duplicate address)" : (dustAttack ? " (Possible dust attack)" : ""));
|
||||
}
|
||||
|
||||
public static Glyph getDuplicateGlyph() {
|
||||
|
@ -60,4 +62,11 @@ public class AddressCell extends TreeTableCell<Entry, UtxoEntry.AddressStatus> {
|
|||
duplicateGlyph.setFontSize(12);
|
||||
return duplicateGlyph;
|
||||
}
|
||||
|
||||
public static Glyph getDustAttackGlyph() {
|
||||
Glyph dustAttackGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.EXCLAMATION_TRIANGLE);
|
||||
dustAttackGlyph.getStyleClass().add("dust-attack-warning");
|
||||
dustAttackGlyph.setFontSize(12);
|
||||
return dustAttackGlyph;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import java.util.stream.Collectors;
|
|||
import static com.sparrowwallet.sparrow.AppServices.ENUMERATE_HW_PERIOD_SECS;
|
||||
import static com.sparrowwallet.sparrow.net.PagedBatchRequestBuilder.DEFAULT_PAGE_SIZE;
|
||||
import static com.sparrowwallet.sparrow.net.TcpTransport.DEFAULT_MAX_TIMEOUT;
|
||||
import static com.sparrowwallet.sparrow.wallet.WalletUtxosEntry.DUST_ATTACK_THRESHOLD_SATS;
|
||||
|
||||
public class Config {
|
||||
private static final Logger log = LoggerFactory.getLogger(Config.class);
|
||||
|
@ -45,6 +46,7 @@ public class Config {
|
|||
private boolean preventSleep = false;
|
||||
private List<File> recentWalletFiles;
|
||||
private Integer keyDerivationPeriod;
|
||||
private long dustAttackThreshold = DUST_ATTACK_THRESHOLD_SATS;
|
||||
private File hwi;
|
||||
private int enumerateHwPeriod = ENUMERATE_HW_PERIOD_SECS;
|
||||
private Boolean hdCapture;
|
||||
|
@ -302,6 +304,10 @@ public class Config {
|
|||
flush();
|
||||
}
|
||||
|
||||
public long getDustAttackThreshold() {
|
||||
return dustAttackThreshold;
|
||||
}
|
||||
|
||||
public File getHwi() {
|
||||
return hwi;
|
||||
}
|
||||
|
|
|
@ -69,7 +69,13 @@ public class UtxoEntry extends HashIndexEntry {
|
|||
private ObjectProperty<AddressStatus> addressStatusProperty;
|
||||
|
||||
public final void setDuplicateAddress(boolean value) {
|
||||
addressStatusProperty().set(new AddressStatus(value));
|
||||
AddressStatus addressStatus = addressStatusProperty().get();
|
||||
addressStatusProperty().set(new AddressStatus(value, addressStatus.dustAttack));
|
||||
}
|
||||
|
||||
public final void setDustAttack(boolean value) {
|
||||
AddressStatus addressStatus = addressStatusProperty().get();
|
||||
addressStatusProperty().set(new AddressStatus(addressStatus.duplicate, value));
|
||||
}
|
||||
|
||||
public final boolean isDuplicateAddress() {
|
||||
|
@ -78,7 +84,7 @@ public class UtxoEntry extends HashIndexEntry {
|
|||
|
||||
public final ObjectProperty<AddressStatus> addressStatusProperty() {
|
||||
if(addressStatusProperty == null) {
|
||||
addressStatusProperty = new SimpleObjectProperty<>(UtxoEntry.this, "addressStatus", new AddressStatus(false));
|
||||
addressStatusProperty = new SimpleObjectProperty<>(UtxoEntry.this, "addressStatus", new AddressStatus(false, false));
|
||||
}
|
||||
|
||||
return addressStatusProperty;
|
||||
|
@ -86,9 +92,11 @@ public class UtxoEntry extends HashIndexEntry {
|
|||
|
||||
public class AddressStatus {
|
||||
private final boolean duplicate;
|
||||
private final boolean dustAttack;
|
||||
|
||||
public AddressStatus(boolean duplicate) {
|
||||
public AddressStatus(boolean duplicate, boolean dustAttack) {
|
||||
this.duplicate = duplicate;
|
||||
this.dustAttack = dustAttack;
|
||||
}
|
||||
|
||||
public UtxoEntry getUtxoEntry() {
|
||||
|
@ -102,6 +110,10 @@ public class UtxoEntry extends HashIndexEntry {
|
|||
public boolean isDuplicate() {
|
||||
return duplicate;
|
||||
}
|
||||
|
||||
public boolean isDustAttack() {
|
||||
return dustAttack;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,16 +2,21 @@ package com.sparrowwallet.sparrow.wallet;
|
|||
|
||||
import com.samourai.whirlpool.client.wallet.beans.MixProgress;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.drongo.wallet.WalletNode;
|
||||
import com.sparrowwallet.sparrow.AppServices;
|
||||
import com.sparrowwallet.sparrow.io.Config;
|
||||
import com.sparrowwallet.sparrow.whirlpool.Whirlpool;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class WalletUtxosEntry extends Entry {
|
||||
public static final int DUST_ATTACK_THRESHOLD_SATS = 1000;
|
||||
|
||||
public WalletUtxosEntry(Wallet wallet) {
|
||||
super(wallet, wallet.getName(), wallet.getWalletUtxos().entrySet().stream().map(entry -> new UtxoEntry(entry.getValue().getWallet(), entry.getKey(), HashIndexEntry.Type.OUTPUT, entry.getValue())).collect(Collectors.toList()));
|
||||
calculateDuplicates();
|
||||
calculateDust();
|
||||
updateMixProgress();
|
||||
}
|
||||
|
||||
|
@ -48,6 +53,18 @@ public class WalletUtxosEntry extends Entry {
|
|||
}
|
||||
}
|
||||
|
||||
protected void calculateDust() {
|
||||
long dustAttackThreshold = Config.get().getDustAttackThreshold();
|
||||
Set<WalletNode> duplicateNodes = getWallet().getWalletTxos().values().stream()
|
||||
.collect(Collectors.groupingBy(e -> e, Collectors.counting()))
|
||||
.entrySet().stream().filter(e -> e.getValue() > 1).map(Map.Entry::getKey).collect(Collectors.toSet());
|
||||
|
||||
for(Entry entry : getChildren()) {
|
||||
UtxoEntry utxoEntry = (UtxoEntry) entry;
|
||||
utxoEntry.setDustAttack(utxoEntry.getValue() <= dustAttackThreshold && duplicateNodes.contains(utxoEntry.getNode()));
|
||||
}
|
||||
}
|
||||
|
||||
public void updateMixProgress() {
|
||||
Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(getWallet());
|
||||
if(whirlpool != null) {
|
||||
|
@ -74,6 +91,7 @@ public class WalletUtxosEntry extends Entry {
|
|||
getChildren().removeAll(entriesRemoved);
|
||||
|
||||
calculateDuplicates();
|
||||
calculateDust();
|
||||
updateMixProgress();
|
||||
}
|
||||
|
||||
|
|
|
@ -123,6 +123,10 @@
|
|||
-fx-text-fill: rgb(202, 18, 67);
|
||||
}
|
||||
|
||||
.dust-attack-warning {
|
||||
-fx-text-fill: rgb(238, 210, 2);
|
||||
}
|
||||
|
||||
.unused-check {
|
||||
-fx-text-fill: #50a14f;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue