mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-24 12:46:45 +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())));
|
setContextMenu(new EntryCell.AddressContextMenu(address, utxoEntry.getOutputDescriptor(), new NodeEntry(utxoEntry.getWallet(), utxoEntry.getNode())));
|
||||||
Tooltip tooltip = new Tooltip();
|
Tooltip tooltip = new Tooltip();
|
||||||
tooltip.setShowDelay(Duration.millis(250));
|
tooltip.setShowDelay(Duration.millis(250));
|
||||||
tooltip.setText(getTooltipText(utxoEntry, addressStatus.isDuplicate()));
|
tooltip.setText(getTooltipText(utxoEntry, addressStatus.isDuplicate(), addressStatus.isDustAttack()));
|
||||||
setTooltip(tooltip);
|
setTooltip(tooltip);
|
||||||
|
|
||||||
if(addressStatus.isDuplicate()) {
|
if(addressStatus.isDuplicate()) {
|
||||||
setGraphic(getDuplicateGlyph());
|
setGraphic(getDuplicateGlyph());
|
||||||
|
} else if(addressStatus.isDustAttack()) {
|
||||||
|
setGraphic(getDustAttackGlyph());
|
||||||
} else {
|
} else {
|
||||||
setGraphic(null);
|
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() + " " : "" ) +
|
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() {
|
public static Glyph getDuplicateGlyph() {
|
||||||
|
@ -60,4 +62,11 @@ public class AddressCell extends TreeTableCell<Entry, UtxoEntry.AddressStatus> {
|
||||||
duplicateGlyph.setFontSize(12);
|
duplicateGlyph.setFontSize(12);
|
||||||
return duplicateGlyph;
|
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.AppServices.ENUMERATE_HW_PERIOD_SECS;
|
||||||
import static com.sparrowwallet.sparrow.net.PagedBatchRequestBuilder.DEFAULT_PAGE_SIZE;
|
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.net.TcpTransport.DEFAULT_MAX_TIMEOUT;
|
||||||
|
import static com.sparrowwallet.sparrow.wallet.WalletUtxosEntry.DUST_ATTACK_THRESHOLD_SATS;
|
||||||
|
|
||||||
public class Config {
|
public class Config {
|
||||||
private static final Logger log = LoggerFactory.getLogger(Config.class);
|
private static final Logger log = LoggerFactory.getLogger(Config.class);
|
||||||
|
@ -45,6 +46,7 @@ public class Config {
|
||||||
private boolean preventSleep = false;
|
private boolean preventSleep = false;
|
||||||
private List<File> recentWalletFiles;
|
private List<File> recentWalletFiles;
|
||||||
private Integer keyDerivationPeriod;
|
private Integer keyDerivationPeriod;
|
||||||
|
private long dustAttackThreshold = DUST_ATTACK_THRESHOLD_SATS;
|
||||||
private File hwi;
|
private File hwi;
|
||||||
private int enumerateHwPeriod = ENUMERATE_HW_PERIOD_SECS;
|
private int enumerateHwPeriod = ENUMERATE_HW_PERIOD_SECS;
|
||||||
private Boolean hdCapture;
|
private Boolean hdCapture;
|
||||||
|
@ -302,6 +304,10 @@ public class Config {
|
||||||
flush();
|
flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getDustAttackThreshold() {
|
||||||
|
return dustAttackThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
public File getHwi() {
|
public File getHwi() {
|
||||||
return hwi;
|
return hwi;
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,13 @@ public class UtxoEntry extends HashIndexEntry {
|
||||||
private ObjectProperty<AddressStatus> addressStatusProperty;
|
private ObjectProperty<AddressStatus> addressStatusProperty;
|
||||||
|
|
||||||
public final void setDuplicateAddress(boolean value) {
|
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() {
|
public final boolean isDuplicateAddress() {
|
||||||
|
@ -78,7 +84,7 @@ public class UtxoEntry extends HashIndexEntry {
|
||||||
|
|
||||||
public final ObjectProperty<AddressStatus> addressStatusProperty() {
|
public final ObjectProperty<AddressStatus> addressStatusProperty() {
|
||||||
if(addressStatusProperty == null) {
|
if(addressStatusProperty == null) {
|
||||||
addressStatusProperty = new SimpleObjectProperty<>(UtxoEntry.this, "addressStatus", new AddressStatus(false));
|
addressStatusProperty = new SimpleObjectProperty<>(UtxoEntry.this, "addressStatus", new AddressStatus(false, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
return addressStatusProperty;
|
return addressStatusProperty;
|
||||||
|
@ -86,9 +92,11 @@ public class UtxoEntry extends HashIndexEntry {
|
||||||
|
|
||||||
public class AddressStatus {
|
public class AddressStatus {
|
||||||
private final boolean duplicate;
|
private final boolean duplicate;
|
||||||
|
private final boolean dustAttack;
|
||||||
|
|
||||||
public AddressStatus(boolean duplicate) {
|
public AddressStatus(boolean duplicate, boolean dustAttack) {
|
||||||
this.duplicate = duplicate;
|
this.duplicate = duplicate;
|
||||||
|
this.dustAttack = dustAttack;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UtxoEntry getUtxoEntry() {
|
public UtxoEntry getUtxoEntry() {
|
||||||
|
@ -102,6 +110,10 @@ public class UtxoEntry extends HashIndexEntry {
|
||||||
public boolean isDuplicate() {
|
public boolean isDuplicate() {
|
||||||
return duplicate;
|
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.samourai.whirlpool.client.wallet.beans.MixProgress;
|
||||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
|
import com.sparrowwallet.drongo.wallet.WalletNode;
|
||||||
import com.sparrowwallet.sparrow.AppServices;
|
import com.sparrowwallet.sparrow.AppServices;
|
||||||
|
import com.sparrowwallet.sparrow.io.Config;
|
||||||
import com.sparrowwallet.sparrow.whirlpool.Whirlpool;
|
import com.sparrowwallet.sparrow.whirlpool.Whirlpool;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class WalletUtxosEntry extends Entry {
|
public class WalletUtxosEntry extends Entry {
|
||||||
|
public static final int DUST_ATTACK_THRESHOLD_SATS = 1000;
|
||||||
|
|
||||||
public WalletUtxosEntry(Wallet wallet) {
|
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()));
|
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();
|
calculateDuplicates();
|
||||||
|
calculateDust();
|
||||||
updateMixProgress();
|
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() {
|
public void updateMixProgress() {
|
||||||
Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(getWallet());
|
Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(getWallet());
|
||||||
if(whirlpool != null) {
|
if(whirlpool != null) {
|
||||||
|
@ -74,6 +91,7 @@ public class WalletUtxosEntry extends Entry {
|
||||||
getChildren().removeAll(entriesRemoved);
|
getChildren().removeAll(entriesRemoved);
|
||||||
|
|
||||||
calculateDuplicates();
|
calculateDuplicates();
|
||||||
|
calculateDust();
|
||||||
updateMixProgress();
|
updateMixProgress();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -123,6 +123,10 @@
|
||||||
-fx-text-fill: rgb(202, 18, 67);
|
-fx-text-fill: rgb(202, 18, 67);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dust-attack-warning {
|
||||||
|
-fx-text-fill: rgb(238, 210, 2);
|
||||||
|
}
|
||||||
|
|
||||||
.unused-check {
|
.unused-check {
|
||||||
-fx-text-fill: #50a14f;
|
-fx-text-fill: #50a14f;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue