add fee rate selection for premix

This commit is contained in:
Craig Raw 2021-10-22 11:01:58 +02:00
parent 813e0f3ab1
commit 1497b3d3bb
9 changed files with 90 additions and 8 deletions

2
drongo

@ -1 +1 @@
Subproject commit 57290a20a143bc0f0d8440fb3c14592c2e204e6b Subproject commit eb49c9713375ac5f909d8ec3fa4dfddaf7d63ffe

View file

@ -11,6 +11,7 @@ public class SpendUtxoEvent {
private final Wallet wallet; private final Wallet wallet;
private final List<BlockTransactionHashIndex> utxos; private final List<BlockTransactionHashIndex> utxos;
private final List<Payment> payments; private final List<Payment> payments;
private final List<byte[]> opReturns;
private final Long fee; private final Long fee;
private final boolean includeSpentMempoolOutputs; private final boolean includeSpentMempoolOutputs;
private final Pool pool; private final Pool pool;
@ -23,15 +24,17 @@ public class SpendUtxoEvent {
this.wallet = wallet; this.wallet = wallet;
this.utxos = utxos; this.utxos = utxos;
this.payments = payments; this.payments = payments;
this.opReturns = null;
this.fee = fee; this.fee = fee;
this.includeSpentMempoolOutputs = includeSpentMempoolOutputs; this.includeSpentMempoolOutputs = includeSpentMempoolOutputs;
this.pool = null; this.pool = null;
} }
public SpendUtxoEvent(Wallet wallet, List<BlockTransactionHashIndex> utxos, List<Payment> payments, Long fee, Pool pool) { public SpendUtxoEvent(Wallet wallet, List<BlockTransactionHashIndex> utxos, List<Payment> payments, List<byte[]> opReturns, Long fee, Pool pool) {
this.wallet = wallet; this.wallet = wallet;
this.utxos = utxos; this.utxos = utxos;
this.payments = payments; this.payments = payments;
this.opReturns = opReturns;
this.fee = fee; this.fee = fee;
this.includeSpentMempoolOutputs = false; this.includeSpentMempoolOutputs = false;
this.pool = pool; this.pool = pool;
@ -49,6 +52,10 @@ public class SpendUtxoEvent {
return payments; return payments;
} }
public List<byte[]> getOpReturns() {
return opReturns;
}
public Long getFee() { public Long getFee() {
return fee; return fee;
} }

View file

@ -162,6 +162,8 @@ public class SendController extends WalletFormController implements Initializabl
private final BooleanProperty includeSpentMempoolOutputsProperty = new SimpleBooleanProperty(false); private final BooleanProperty includeSpentMempoolOutputsProperty = new SimpleBooleanProperty(false);
private final List<byte[]> opReturnsList = new ArrayList<>();
private final Set<WalletNode> excludedChangeNodes = new HashSet<>(); private final Set<WalletNode> excludedChangeNodes = new HashSet<>();
private final ChangeListener<String> feeListener = new ChangeListener<>() { private final ChangeListener<String> feeListener = new ChangeListener<>() {
@ -556,7 +558,7 @@ public class SendController extends WalletFormController implements Initializabl
boolean includeMempoolOutputs = Config.get().isIncludeMempoolOutputs(); boolean includeMempoolOutputs = Config.get().isIncludeMempoolOutputs();
boolean includeSpentMempoolOutputs = includeSpentMempoolOutputsProperty.get(); boolean includeSpentMempoolOutputs = includeSpentMempoolOutputsProperty.get();
walletTransactionService = new WalletTransactionService(wallet, getUtxoSelectors(payments), getUtxoFilters(), payments, excludedChangeNodes, feeRate, getMinimumFeeRate(), userFee, currentBlockHeight, groupByAddress, includeMempoolOutputs, includeSpentMempoolOutputs); walletTransactionService = new WalletTransactionService(wallet, getUtxoSelectors(payments), getUtxoFilters(), payments, opReturnsList, excludedChangeNodes, feeRate, getMinimumFeeRate(), userFee, currentBlockHeight, groupByAddress, includeMempoolOutputs, includeSpentMempoolOutputs);
walletTransactionService.setOnSucceeded(event -> { walletTransactionService.setOnSucceeded(event -> {
if(!walletTransactionService.isIgnoreResult()) { if(!walletTransactionService.isIgnoreResult()) {
walletTransactionProperty.setValue(walletTransactionService.getValue()); walletTransactionProperty.setValue(walletTransactionService.getValue());
@ -616,6 +618,7 @@ public class SendController extends WalletFormController implements Initializabl
private final List<UtxoSelector> utxoSelectors; private final List<UtxoSelector> utxoSelectors;
private final List<UtxoFilter> utxoFilters; private final List<UtxoFilter> utxoFilters;
private final List<Payment> payments; private final List<Payment> payments;
private final List<byte[]> opReturns;
private final Set<WalletNode> excludedChangeNodes; private final Set<WalletNode> excludedChangeNodes;
private final double feeRate; private final double feeRate;
private final double longTermFeeRate; private final double longTermFeeRate;
@ -626,11 +629,12 @@ public class SendController extends WalletFormController implements Initializabl
private final boolean includeSpentMempoolOutputs; private final boolean includeSpentMempoolOutputs;
private boolean ignoreResult; private boolean ignoreResult;
public WalletTransactionService(Wallet wallet, List<UtxoSelector> utxoSelectors, List<UtxoFilter> utxoFilters, List<Payment> payments, Set<WalletNode> excludedChangeNodes, double feeRate, double longTermFeeRate, Long fee, Integer currentBlockHeight, boolean groupByAddress, boolean includeMempoolOutputs, boolean includeSpentMempoolOutputs) { public WalletTransactionService(Wallet wallet, List<UtxoSelector> utxoSelectors, List<UtxoFilter> utxoFilters, List<Payment> payments, List<byte[]> opReturns, Set<WalletNode> excludedChangeNodes, double feeRate, double longTermFeeRate, Long fee, Integer currentBlockHeight, boolean groupByAddress, boolean includeMempoolOutputs, boolean includeSpentMempoolOutputs) {
this.wallet = wallet; this.wallet = wallet;
this.utxoSelectors = utxoSelectors; this.utxoSelectors = utxoSelectors;
this.utxoFilters = utxoFilters; this.utxoFilters = utxoFilters;
this.payments = payments; this.payments = payments;
this.opReturns = opReturns;
this.excludedChangeNodes = excludedChangeNodes; this.excludedChangeNodes = excludedChangeNodes;
this.feeRate = feeRate; this.feeRate = feeRate;
this.longTermFeeRate = longTermFeeRate; this.longTermFeeRate = longTermFeeRate;
@ -645,7 +649,7 @@ public class SendController extends WalletFormController implements Initializabl
protected Task<WalletTransaction> createTask() { protected Task<WalletTransaction> createTask() {
return new Task<>() { return new Task<>() {
protected WalletTransaction call() throws InsufficientFundsException { protected WalletTransaction call() throws InsufficientFundsException {
return wallet.createWalletTransaction(utxoSelectors, utxoFilters, payments, excludedChangeNodes, feeRate, longTermFeeRate, fee, currentBlockHeight, groupByAddress, includeMempoolOutputs, includeSpentMempoolOutputs); return wallet.createWalletTransaction(utxoSelectors, utxoFilters, payments, opReturns, excludedChangeNodes, feeRate, longTermFeeRate, fee, currentBlockHeight, groupByAddress, includeMempoolOutputs, includeSpentMempoolOutputs);
} }
}; };
} }
@ -1014,6 +1018,7 @@ public class SendController extends WalletFormController implements Initializabl
utxoSelectorProperty.setValue(null); utxoSelectorProperty.setValue(null);
utxoFilterProperty.setValue(null); utxoFilterProperty.setValue(null);
includeSpentMempoolOutputsProperty.set(false); includeSpentMempoolOutputsProperty.set(false);
opReturnsList.clear();
excludedChangeNodes.clear(); excludedChangeNodes.clear();
walletTransactionProperty.setValue(null); walletTransactionProperty.setValue(null);
createdWalletTransactionProperty.set(null); createdWalletTransactionProperty.set(null);
@ -1239,6 +1244,10 @@ public class SendController extends WalletFormController implements Initializabl
setPayments(List.of(payment)); setPayments(List.of(payment));
} }
if(event.getOpReturns() != null) {
opReturnsList.addAll(event.getOpReturns());
}
if(event.getFee() != null) { if(event.getFee() != null) {
setFeeValueSats(event.getFee()); setFeeValueSats(event.getFee());
userFeeSet.set(true); userFeeSet.set(true);

View file

@ -320,10 +320,12 @@ public class UtxosController extends WalletFormController implements Initializab
payments.add(new Payment(premixAddress, "Premix #" + i, tx0Preview.getPremixValue(), false)); payments.add(new Payment(premixAddress, "Premix #" + i, tx0Preview.getPremixValue(), false));
} }
List<byte[]> opReturns = List.of(new byte[64]);
final List<BlockTransactionHashIndex> utxos = utxoEntries.stream().map(HashIndexEntry::getHashIndex).collect(Collectors.toList()); final List<BlockTransactionHashIndex> utxos = utxoEntries.stream().map(HashIndexEntry::getHashIndex).collect(Collectors.toList());
Platform.runLater(() -> { Platform.runLater(() -> {
EventManager.get().post(new SendActionEvent(getWalletForm().getWallet(), utxos)); EventManager.get().post(new SendActionEvent(getWalletForm().getWallet(), utxos));
Platform.runLater(() -> EventManager.get().post(new SpendUtxoEvent(getWalletForm().getWallet(), utxos, payments, tx0Preview.getTx0MinerFee(), tx0Preview.getPool()))); Platform.runLater(() -> EventManager.get().post(new SpendUtxoEvent(getWalletForm().getWallet(), utxos, payments, opReturns, tx0Preview.getTx0MinerFee(), tx0Preview.getPool())));
}); });
} }

View file

@ -71,6 +71,7 @@ public class Whirlpool {
private final Tx0ParamService tx0ParamService; private final Tx0ParamService tx0ParamService;
private final ExpirablePoolSupplier poolSupplier; private final ExpirablePoolSupplier poolSupplier;
private final Tx0Service tx0Service; private final Tx0Service tx0Service;
private Tx0FeeTarget tx0FeeTarget = Tx0FeeTarget.BLOCKS_4;
private HD_Wallet hdWallet; private HD_Wallet hdWallet;
private String walletId; private String walletId;
private String mixToWalletId; private String mixToWalletId;
@ -140,7 +141,6 @@ public class Whirlpool {
} }
private Tx0Config computeTx0Config() { private Tx0Config computeTx0Config() {
Tx0FeeTarget tx0FeeTarget = Tx0FeeTarget.BLOCKS_4;
Tx0FeeTarget mixFeeTarget = Tx0FeeTarget.BLOCKS_4; Tx0FeeTarget mixFeeTarget = Tx0FeeTarget.BLOCKS_4;
return new Tx0Config(tx0ParamService, poolSupplier, tx0FeeTarget, mixFeeTarget, WhirlpoolAccount.BADBANK); return new Tx0Config(tx0ParamService, poolSupplier, tx0FeeTarget, mixFeeTarget, WhirlpoolAccount.BADBANK);
} }
@ -385,6 +385,14 @@ public class Whirlpool {
config.setScode(scode); config.setScode(scode);
} }
public Tx0FeeTarget getTx0FeeTarget() {
return tx0FeeTarget;
}
public void setTx0FeeTarget(Tx0FeeTarget tx0FeeTarget) {
this.tx0FeeTarget = tx0FeeTarget;
}
public String getWalletId() { public String getWalletId() {
return walletId; return walletId;
} }

View file

@ -2,6 +2,7 @@ package com.sparrowwallet.sparrow.whirlpool;
import com.samourai.whirlpool.client.tx0.Tx0Preview; import com.samourai.whirlpool.client.tx0.Tx0Preview;
import com.samourai.whirlpool.client.tx0.Tx0Previews; import com.samourai.whirlpool.client.tx0.Tx0Previews;
import com.samourai.whirlpool.client.wallet.beans.Tx0FeeTarget;
import com.samourai.whirlpool.client.whirlpool.beans.Pool; import com.samourai.whirlpool.client.whirlpool.beans.Pool;
import com.sparrowwallet.drongo.BitcoinUnit; import com.sparrowwallet.drongo.BitcoinUnit;
import com.sparrowwallet.drongo.protocol.Transaction; import com.sparrowwallet.drongo.protocol.Transaction;
@ -10,10 +11,12 @@ import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.control.CoinLabel; import com.sparrowwallet.sparrow.control.CoinLabel;
import com.sparrowwallet.sparrow.control.CopyableLabel;
import com.sparrowwallet.sparrow.event.WalletMasterMixConfigChangedEvent; import com.sparrowwallet.sparrow.event.WalletMasterMixConfigChangedEvent;
import com.sparrowwallet.sparrow.io.Config; import com.sparrowwallet.sparrow.io.Config;
import com.sparrowwallet.sparrow.wallet.Entry; import com.sparrowwallet.sparrow.wallet.Entry;
import com.sparrowwallet.sparrow.wallet.UtxoEntry; import com.sparrowwallet.sparrow.wallet.UtxoEntry;
import com.sparrowwallet.sparrow.whirlpool.dataSource.SparrowMinerFeeSupplier;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
@ -26,6 +29,8 @@ import javafx.util.StringConverter;
import java.util.*; import java.util.*;
public class WhirlpoolController { public class WhirlpoolController {
private static final List<Tx0FeeTarget> FEE_TARGETS = List.of(Tx0FeeTarget.MIN, Tx0FeeTarget.BLOCKS_4, Tx0FeeTarget.BLOCKS_2);
@FXML @FXML
private VBox whirlpoolBox; private VBox whirlpoolBox;
@ -44,6 +49,12 @@ public class WhirlpoolController {
@FXML @FXML
private TextField scode; private TextField scode;
@FXML
private Slider premixPriority;
@FXML
private CopyableLabel premixFeeRate;
@FXML @FXML
private ComboBox<Pool> pool; private ComboBox<Pool> pool;
@ -108,6 +119,30 @@ public class WhirlpoolController {
EventManager.get().post(new WalletMasterMixConfigChangedEvent(wallet)); EventManager.get().post(new WalletMasterMixConfigChangedEvent(wallet));
}); });
premixPriority.setMin(0);
premixPriority.setMax(FEE_TARGETS.size() - 1);
premixPriority.setMajorTickUnit(1);
premixPriority.setMinorTickCount(0);
premixPriority.setLabelFormatter(new StringConverter<>() {
@Override
public String toString(Double object) {
return object.intValue() == 0 ? "Low" : (object.intValue() == 1 ? "Normal" : "High");
}
@Override
public Double fromString(String string) {
return null;
}
});
premixPriority.valueProperty().addListener((observable, oldValue, newValue) -> {
pool.setItems(FXCollections.emptyObservableList());
tx0Previews = null;
tx0PreviewProperty.set(null);
Tx0FeeTarget tx0FeeTarget = FEE_TARGETS.get(newValue.intValue());
premixFeeRate.setText(SparrowMinerFeeSupplier.getMinimumFeeForTarget(Integer.parseInt(tx0FeeTarget.getFeeTarget().getValue())) + " sats/vB");
});
premixPriority.setValue(1);
if(mixConfig.getScode() != null) { if(mixConfig.getScode() != null) {
step1.setVisible(false); step1.setVisible(false);
step3.setVisible(true); step3.setVisible(true);
@ -280,6 +315,7 @@ public class WhirlpoolController {
} else { } else {
tx0Previews = null; tx0Previews = null;
whirlpool.setScode(mixConfig.getScode()); whirlpool.setScode(mixConfig.getScode());
whirlpool.setTx0FeeTarget(FEE_TARGETS.get(premixPriority.valueProperty().intValue()));
Whirlpool.Tx0PreviewsService tx0PreviewsService = new Whirlpool.Tx0PreviewsService(whirlpool, wallet, utxoEntries); Whirlpool.Tx0PreviewsService tx0PreviewsService = new Whirlpool.Tx0PreviewsService(whirlpool, wallet, utxoEntries);
tx0PreviewsService.setOnRunning(workerStateEvent -> { tx0PreviewsService.setOnRunning(workerStateEvent -> {

View file

@ -32,7 +32,7 @@ public class SparrowMinerFeeSupplier implements MinerFeeSupplier {
return getMinimumFeeForTarget(Integer.parseInt(feeTarget.getValue())); return getMinimumFeeForTarget(Integer.parseInt(feeTarget.getValue()));
} }
private Integer getMinimumFeeForTarget(int targetBlocks) { public static Integer getMinimumFeeForTarget(int targetBlocks) {
List<Map.Entry<Integer, Double>> feeRates = new ArrayList<>(AppServices.getTargetBlockFeeRates().entrySet()); List<Map.Entry<Integer, Double>> feeRates = new ArrayList<>(AppServices.getTargetBlockFeeRates().entrySet());
Collections.reverse(feeRates); Collections.reverse(feeRates);
for(Map.Entry<Integer, Double> feeRate : feeRates) { for(Map.Entry<Integer, Double> feeRate : feeRates) {
@ -40,6 +40,7 @@ public class SparrowMinerFeeSupplier implements MinerFeeSupplier {
return feeRate.getValue().intValue(); return feeRate.getValue().intValue();
} }
} }
return feeRates.get(0).getValue().intValue(); return feeRates.get(0).getValue().intValue();
} }

View file

@ -39,4 +39,8 @@
.field-label { .field-label {
-fx-pref-width: 120px; -fx-pref-width: 120px;
}
.field-control {
-fx-pref-width: 160px;
} }

View file

@ -11,6 +11,7 @@
<?import javafx.scene.image.Image?> <?import javafx.scene.image.Image?>
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import com.sparrowwallet.sparrow.control.CoinLabel?> <?import com.sparrowwallet.sparrow.control.CoinLabel?>
<?import com.sparrowwallet.sparrow.control.CopyableLabel?>
<StackPane prefHeight="460.0" prefWidth="600.0" stylesheets="@whirlpool.css, @../general.css" styleClass="whirlpool-pane" fx:controller="com.sparrowwallet.sparrow.whirlpool.WhirlpoolController" xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"> <StackPane prefHeight="460.0" prefWidth="600.0" stylesheets="@whirlpool.css, @../general.css" styleClass="whirlpool-pane" fx:controller="com.sparrowwallet.sparrow.whirlpool.WhirlpoolController" xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml">
<VBox spacing="20"> <VBox spacing="20">
<HBox styleClass="title-area"> <HBox styleClass="title-area">
@ -93,6 +94,20 @@
<Label text="SCODE:" styleClass="field-label" /> <Label text="SCODE:" styleClass="field-label" />
<TextField fx:id="scode" /> <TextField fx:id="scode" />
</HBox> </HBox>
<HBox styleClass="field-box">
<padding>
<Insets top="10" />
</padding>
<Label text="Premix Priority:" styleClass="field-label" />
<Slider fx:id="premixPriority" snapToTicks="true" showTickLabels="true" showTickMarks="true" styleClass="field-control" />
</HBox>
<HBox styleClass="field-box">
<padding>
<Insets top="10" />
</padding>
<Label text="Premix Fee Rate:" styleClass="field-label" />
<CopyableLabel fx:id="premixFeeRate" />
</HBox>
</VBox> </VBox>
<VBox fx:id="step4" spacing="15"> <VBox fx:id="step4" spacing="15">
<Label text="Select Pool" styleClass="title-text"> <Label text="Select Pool" styleClass="title-text">