Allows minimum rate when connecting to at least one low-rate node

This commit is contained in:
Thauan Amorim 2025-07-16 12:57:25 -03:00
parent 4239c02aff
commit f03883fb7e
4 changed files with 51 additions and 26 deletions

View file

@ -16,7 +16,6 @@ import com.sparrowwallet.drongo.wallet.*;
import com.sparrowwallet.sparrow.control.DialogImage; import com.sparrowwallet.sparrow.control.DialogImage;
import com.sparrowwallet.sparrow.control.WalletPasswordDialog; import com.sparrowwallet.sparrow.control.WalletPasswordDialog;
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5; import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
import com.sparrowwallet.sparrow.net.Auth47;
import com.sparrowwallet.drongo.protocol.BlockHeader; import com.sparrowwallet.drongo.protocol.BlockHeader;
import com.sparrowwallet.drongo.protocol.ScriptType; import com.sparrowwallet.drongo.protocol.ScriptType;
import com.sparrowwallet.drongo.protocol.Transaction; import com.sparrowwallet.drongo.protocol.Transaction;
@ -71,6 +70,7 @@ import java.util.List;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.sparrowwallet.sparrow.control.DownloadVerifierDialog.*; import static com.sparrowwallet.sparrow.control.DownloadVerifierDialog.*;
@ -91,8 +91,11 @@ public class AppServices {
private static final String TOR_DEFAULT_PROXY_CIRCUIT_ID = "default"; private static final String TOR_DEFAULT_PROXY_CIRCUIT_ID = "default";
public static final List<Integer> TARGET_BLOCKS_RANGE = List.of(1, 2, 3, 4, 5, 10, 25, 50); public static final List<Integer> TARGET_BLOCKS_RANGE = List.of(1, 2, 3, 4, 5, 10, 25, 50);
public static final List<Double> DOUBLE_FEE_RATES_RANGE = List.of(0.01D, 0.05D, 0.1D, 0.5, 1D, 2D, 4D, 8D, 16D, 32D, 64D, 128D, 256D, 512D, 1024D, 2048D, 4096D, 8192D); public static final List<Double> DOUBLE_FEE_MIN_RATES_RANGE = List.of(0.01D, 0.05D, 0.1D, 0.5);
public static final List<Double> FEE_RATES_RANGE = DOUBLE_FEE_RATES_RANGE.subList(0, DOUBLE_FEE_RATES_RANGE.size() - 8); public static final List<Double> DOUBLE_FEE_RATES_RANGE = List.of(1D, 2D, 4D, 8D, 16D, 32D, 64D, 128D, 256D, 512D, 1024D, 2048D, 4096D, 8192D);
public static final List<Double> FULL_DOUBLE_FEE_RATES_RANGE = Stream.concat(DOUBLE_FEE_MIN_RATES_RANGE.stream(), DOUBLE_FEE_RATES_RANGE.stream()).toList();
public static final List<Double> FEE_RATES_RANGE = DOUBLE_FEE_RATES_RANGE.subList(0, DOUBLE_FEE_RATES_RANGE.size() - 4);
public static final List<Double> FULL_FEE_RATES_RANGE = FULL_DOUBLE_FEE_RATES_RANGE.subList(0, FULL_DOUBLE_FEE_RATES_RANGE.size() - 8);
public static final double FALLBACK_FEE_RATE = 20000d / 1000; public static final double FALLBACK_FEE_RATE = 20000d / 1000;
public static final double TESTNET_FALLBACK_FEE_RATE = 1000d / 1000; public static final double TESTNET_FALLBACK_FEE_RATE = 1000d / 1000;
@ -792,6 +795,24 @@ public class AppServices {
return minimumRelayFeeRate == null ? Transaction.DEFAULT_MIN_RELAY_FEE : minimumRelayFeeRate; return minimumRelayFeeRate == null ? Transaction.DEFAULT_MIN_RELAY_FEE : minimumRelayFeeRate;
} }
public static List<Double> getDoubleFeeRatesRange() {
if (AppServices.getMinimumRelayFeeRate() == 0.01) {
return FULL_DOUBLE_FEE_RATES_RANGE;
}
return DOUBLE_FEE_RATES_RANGE;
}
public static List<Double> getFeeRatesRange() {
if (isFullFeeRatesRange()) {
return FULL_FEE_RATES_RANGE;
}
return FEE_RATES_RANGE;
}
public static boolean isFullFeeRatesRange() {
return getDoubleFeeRatesRange().size() == FULL_DOUBLE_FEE_RATES_RANGE.size();
}
public static CurrencyRate getFiatCurrencyExchangeRate() { public static CurrencyRate getFiatCurrencyExchangeRate() {
return fiatCurrencyExchangeRate; return fiatCurrencyExchangeRate;
} }
@ -1219,7 +1240,7 @@ public class AppServices {
public void newConnection(ConnectionEvent event) { public void newConnection(ConnectionEvent event) {
currentBlockHeight = event.getBlockHeight(); currentBlockHeight = event.getBlockHeight();
System.setProperty(Network.BLOCK_HEIGHT_PROPERTY, Integer.toString(currentBlockHeight)); System.setProperty(Network.BLOCK_HEIGHT_PROPERTY, Integer.toString(currentBlockHeight));
minimumRelayFeeRate = Math.max(event.getMinimumRelayFeeRate(), Transaction.DEFAULT_MIN_RELAY_FEE); minimumRelayFeeRate = Math.min(event.getMinimumRelayFeeRate(), Transaction.DEFAULT_MIN_RELAY_FEE);
latestBlockHeader = event.getBlockHeader(); latestBlockHeader = event.getBlockHeader();
Config.get().addRecentServer(); Config.get().addRecentServer();

View file

@ -16,7 +16,7 @@ public class FeeRangeSlider extends Slider {
private static final double FEE_RATE_SCROLL_INCREMENT = 0.01; private static final double FEE_RATE_SCROLL_INCREMENT = 0.01;
public FeeRangeSlider() { public FeeRangeSlider() {
super(0, FEE_RATES_RANGE.size() - 1, 0); super(0, getFeeRatesRange().size() - 1, 0);
setMajorTickUnit(1); setMajorTickUnit(1);
setMinorTickCount(0); setMinorTickCount(0);
setSnapToTicks(false); setSnapToTicks(false);
@ -27,7 +27,7 @@ public class FeeRangeSlider extends Slider {
setLabelFormatter(new StringConverter<>() { setLabelFormatter(new StringConverter<>() {
@Override @Override
public String toString(Double object) { public String toString(Double object) {
Double feeRate = DOUBLE_FEE_RATES_RANGE.get(object.intValue()); Double feeRate = getDoubleFeeRatesRange().get(object.intValue());
if(isDoubleFeeRange() && feeRate >= 1000) { if(isDoubleFeeRange() && feeRate >= 1000) {
return feeRate.longValue() / 1000 + "k"; return feeRate.longValue() / 1000 + "k";
} }
@ -51,10 +51,10 @@ public class FeeRangeSlider extends Slider {
setOnScroll(event -> { setOnScroll(event -> {
if(event.getDeltaY() != 0) { if(event.getDeltaY() != 0) {
double newFeeRate = getFeeRate() + (event.getDeltaY() > 0 ? FEE_RATE_SCROLL_INCREMENT : -FEE_RATE_SCROLL_INCREMENT); double newFeeRate = getFeeRate() + (event.getDeltaY() > 0 ? FEE_RATE_SCROLL_INCREMENT : -FEE_RATE_SCROLL_INCREMENT);
if(newFeeRate < DOUBLE_FEE_RATES_RANGE.get(0)) { if(newFeeRate < getDoubleFeeRatesRange().get(0)) {
newFeeRate = DOUBLE_FEE_RATES_RANGE.get(0); newFeeRate = getDoubleFeeRatesRange().get(0);
} else if(newFeeRate > DOUBLE_FEE_RATES_RANGE.get(DOUBLE_FEE_RATES_RANGE.size() - 1)) { } else if(newFeeRate > getDoubleFeeRatesRange().get(getDoubleFeeRatesRange().size() - 1)) {
newFeeRate = DOUBLE_FEE_RATES_RANGE.get(DOUBLE_FEE_RATES_RANGE.size() - 1); newFeeRate = getDoubleFeeRatesRange().get(getDoubleFeeRatesRange().size() - 1);
} }
setFeeRate(newFeeRate); setFeeRate(newFeeRate);
} }
@ -63,14 +63,18 @@ public class FeeRangeSlider extends Slider {
public double getFeeRate() { public double getFeeRate() {
double value = getValue(); double value = getValue();
// First range: 0.01, 0.05, 0.1 if (isFullFeeRatesRange()) {
if(value < 1) return 0.01 + (0.05 - 0.01) * value; // First range: 0.01, 0.05, 0.1
if(value < 2) return 0.05 + (0.1 - 0.05) * (value - 1); if(value < 1) return 0.01 + (0.05 - 0.01) * value;
// Second range: 0.1, 0.5, 1 if(value < 2) return 0.05 + (0.1 - 0.05) * (value - 1);
if(value < 3) return 0.1 + (0.5 - 0.1) * (value - 2); // Second range: 0.1, 0.5, 1
if(value < 4) return 0.5 + (1.0 - 0.5) * (value - 3); if(value < 3) return 0.1 + (0.5 - 0.1) * (value - 2);
// Third range: 1, 2, 4, 8, ... if(value < 4) return 0.5 + (1.0 - 0.5) * (value - 3);
return Math.pow(2, value - 4 + 0) * 1.0; // Third range: 1, 2, 4, 8, ...
return Math.pow(2, value - 4 + 0) * 1.0;
}
return Math.pow(2.0, value);
} }
public void setFeeRate(double feeRate) { public void setFeeRate(double feeRate) {
@ -81,18 +85,18 @@ public class FeeRangeSlider extends Slider {
private void updateMaxFeeRange(double value) { private void updateMaxFeeRange(double value) {
if(value >= getMax() && !isDoubleFeeRange()) { if(value >= getMax() && !isDoubleFeeRange()) {
setMin(FEE_RATES_RANGE.size() - 2); setMin(getFeeRatesRange().size() - 2);
setMax(DOUBLE_FEE_RATES_RANGE.size() - 1); setMax(getDoubleFeeRatesRange().size() - 1);
updateTrackHighlight(); updateTrackHighlight();
} else if(value == getMin() && isDoubleFeeRange()) { } else if(value == getMin() && isDoubleFeeRange()) {
setMin(0); setMin(0);
setMax(FEE_RATES_RANGE.size() - 1); setMax(getFeeRatesRange().size() - 1);
updateTrackHighlight(); updateTrackHighlight();
} }
} }
private boolean isDoubleFeeRange() { private boolean isDoubleFeeRange() {
return getMax() > FEE_RATES_RANGE.size() - 1; return getMax() > getFeeRatesRange().size() - 1;
} }
public void updateTrackHighlight() { public void updateTrackHighlight() {
@ -149,7 +153,7 @@ public class FeeRangeSlider extends Slider {
private int getPercentageOfFeeRange(Double feeRate) { private int getPercentageOfFeeRange(Double feeRate) {
double index = Math.log(feeRate) / Math.log(2); double index = Math.log(feeRate) / Math.log(2);
if(isDoubleFeeRange()) { if(isDoubleFeeRange()) {
index *= ((double)FEE_RATES_RANGE.size() / (DOUBLE_FEE_RATES_RANGE.size())) * 0.99; index *= ((double)getFeeRatesRange().size() / (getDoubleFeeRatesRange().size())) * 0.99;
} }
return (int)Math.round(index * 10.0); return (int)Math.round(index * 10.0);
} }

View file

@ -1139,7 +1139,7 @@ public class HeadersController extends TransactionFormController implements Init
if(fee.getValue() > 0) { if(fee.getValue() > 0) {
double feeRateAmt = fee.getValue() / headersForm.getTransaction().getVirtualSize(); double feeRateAmt = fee.getValue() / headersForm.getTransaction().getVirtualSize();
if(feeRateAmt > AppServices.DOUBLE_FEE_RATES_RANGE.get(AppServices.DOUBLE_FEE_RATES_RANGE.size() - 1)) { if(feeRateAmt > AppServices.getDoubleFeeRatesRange().get(AppServices.getDoubleFeeRatesRange().size() - 1)) {
Optional<ButtonType> optType = AppServices.showWarningDialog("Very high fee rate!", Optional<ButtonType> optType = AppServices.showWarningDialog("Very high fee rate!",
"This transaction pays a very high fee rate of " + String.format("%.0f", feeRateAmt) + " sats/vB.\n\nBroadcast this transaction?", ButtonType.YES, ButtonType.NO); "This transaction pays a very high fee rate of " + String.format("%.0f", feeRateAmt) + " sats/vB.\n\nBroadcast this transaction?", ButtonType.YES, ButtonType.NO);
if(optType.isPresent() && optType.get() == ButtonType.NO) { if(optType.isPresent() && optType.get() == ButtonType.NO) {

View file

@ -950,7 +950,7 @@ public class SendController extends WalletFormController implements Initializabl
Map<Integer, Double> targetBlocksFeeRates = getTargetBlocksFeeRates(); Map<Integer, Double> targetBlocksFeeRates = getTargetBlocksFeeRates();
if(targetBlocksFeeRates.get(Integer.MAX_VALUE) != null) { if(targetBlocksFeeRates.get(Integer.MAX_VALUE) != null) {
Double minFeeRate = targetBlocksFeeRates.get(Integer.MAX_VALUE); Double minFeeRate = targetBlocksFeeRates.get(Integer.MAX_VALUE);
if(feeRateAmt > 0.01 && feeRateAmt < minFeeRate) { if(feeRateAmt > getMinimumRelayFeeRate() && feeRateAmt < minFeeRate) {
feeRatePriority.setText("Below Minimum"); feeRatePriority.setText("Below Minimum");
feeRatePriority.setTooltip(new Tooltip("Transactions at this fee rate can be purged from mempool")); feeRatePriority.setTooltip(new Tooltip("Transactions at this fee rate can be purged from mempool"));
feeRatePriorityGlyph.setStyle("-fx-text-fill: #a0a1a7cc"); feeRatePriorityGlyph.setStyle("-fx-text-fill: #a0a1a7cc");
@ -971,7 +971,7 @@ public class SendController extends WalletFormController implements Initializabl
Integer targetBlocks = getTargetBlocks(feeRateAmt); Integer targetBlocks = getTargetBlocks(feeRateAmt);
if(targetBlocks != null) { if(targetBlocks != null) {
if(targetBlocks < FeeRatesSource.BLOCKS_IN_HALF_HOUR) { if(targetBlocks < FeeRatesSource.BLOCKS_IN_HALF_HOUR) {
Double maxFeeRate = FEE_RATES_RANGE.get(FEE_RATES_RANGE.size() - 1).doubleValue(); Double maxFeeRate = getFeeRatesRange().get(getFeeRatesRange().size() - 1).doubleValue();
Double highestBlocksRate = targetBlocksFeeRates.get(TARGET_BLOCKS_RANGE.get(0)); Double highestBlocksRate = targetBlocksFeeRates.get(TARGET_BLOCKS_RANGE.get(0));
if(highestBlocksRate < maxFeeRate && feeRateAmt > (highestBlocksRate + ((maxFeeRate - highestBlocksRate) / 10))) { if(highestBlocksRate < maxFeeRate && feeRateAmt > (highestBlocksRate + ((maxFeeRate - highestBlocksRate) / 10))) {
feeRatePriority.setText("Overpaid"); feeRatePriority.setText("Overpaid");