use cached tx0previews, only save mixconfig on apply

This commit is contained in:
Craig Raw 2021-09-20 15:30:13 +02:00
parent 1c1099217b
commit cfd06a8513
7 changed files with 98 additions and 56 deletions

View file

@ -91,7 +91,7 @@ dependencies {
implementation('org.slf4j:jul-to-slf4j:1.7.30') { implementation('org.slf4j:jul-to-slf4j:1.7.30') {
exclude group: 'org.slf4j' exclude group: 'org.slf4j'
} }
implementation('com.sparrowwallet.nightjar:nightjar:0.2.16-SNAPSHOT') implementation('com.sparrowwallet.nightjar:nightjar:0.2.17-SNAPSHOT')
testImplementation('junit:junit:4.12') testImplementation('junit:junit:4.12')
} }
@ -449,7 +449,7 @@ extraJavaModuleInfo {
module('cbor-0.9.jar', 'co.nstant.in.cbor', '0.9') { module('cbor-0.9.jar', 'co.nstant.in.cbor', '0.9') {
exports('co.nstant.in.cbor') exports('co.nstant.in.cbor')
} }
module('nightjar-0.2.16-SNAPSHOT.jar', 'com.sparrowwallet.nightjar', '0.2.16-SNAPSHOT') { module('nightjar-0.2.17-SNAPSHOT.jar', 'com.sparrowwallet.nightjar', '0.2.17-SNAPSHOT') {
requires('com.google.common') requires('com.google.common')
requires('net.sourceforge.streamsupport') requires('net.sourceforge.streamsupport')
requires('org.slf4j') requires('org.slf4j')

View file

@ -0,0 +1,15 @@
package com.sparrowwallet.sparrow.event;
import com.sparrowwallet.drongo.wallet.Wallet;
public class MixToConfigChangedEvent {
private final Wallet wallet;
public MixToConfigChangedEvent(Wallet wallet) {
this.wallet = wallet;
}
public Wallet getWallet() {
return wallet;
}
}

View file

@ -1,12 +1,11 @@
package com.sparrowwallet.sparrow.wallet; package com.sparrowwallet.sparrow.wallet;
import com.sparrowwallet.drongo.policy.PolicyType;
import com.sparrowwallet.drongo.wallet.MixConfig; import com.sparrowwallet.drongo.wallet.MixConfig;
import com.sparrowwallet.drongo.wallet.StandardAccount; import com.sparrowwallet.drongo.wallet.StandardAccount;
import com.sparrowwallet.drongo.wallet.Wallet; 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.event.WalletMasterMixConfigChangedEvent; import com.sparrowwallet.sparrow.event.MixToConfigChangedEvent;
import com.sparrowwallet.sparrow.whirlpool.Whirlpool; import com.sparrowwallet.sparrow.whirlpool.Whirlpool;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.fxml.FXML; import javafx.fxml.FXML;
@ -31,13 +30,15 @@ public class MixToController implements Initializable {
@FXML @FXML
private Spinner<Integer> minMixes; private Spinner<Integer> minMixes;
private MixConfig mixConfig;
@Override @Override
public void initialize(URL location, ResourceBundle resources) { public void initialize(URL location, ResourceBundle resources) {
} }
public void initializeView(Wallet wallet) { public void initializeView(Wallet wallet) {
MixConfig mixConfig = wallet.getMasterMixConfig(); mixConfig = wallet.getMasterMixConfig().copy();
List<Wallet> allWallets = new ArrayList<>(); List<Wallet> allWallets = new ArrayList<>();
allWallets.add(NONE_WALLET); allWallets.add(NONE_WALLET);
@ -71,14 +72,18 @@ public class MixToController implements Initializable {
mixConfig.setMixToWalletFile(AppServices.get().getOpenWallets().get(newValue).getWalletFile()); mixConfig.setMixToWalletFile(AppServices.get().getOpenWallets().get(newValue).getWalletFile());
} }
EventManager.get().post(new WalletMasterMixConfigChangedEvent(wallet)); EventManager.get().post(new MixToConfigChangedEvent(wallet));
}); });
int initialMinMixes = mixConfig.getMinMixes() == null ? Whirlpool.DEFAULT_MIXTO_MIN_MIXES : mixConfig.getMinMixes(); int initialMinMixes = mixConfig.getMinMixes() == null ? Whirlpool.DEFAULT_MIXTO_MIN_MIXES : mixConfig.getMinMixes();
minMixes.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 10000, initialMinMixes)); minMixes.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 10000, initialMinMixes));
minMixes.valueProperty().addListener((observable, oldValue, newValue) -> { minMixes.valueProperty().addListener((observable, oldValue, newValue) -> {
mixConfig.setMinMixes(newValue); mixConfig.setMinMixes(newValue);
EventManager.get().post(new WalletMasterMixConfigChangedEvent(wallet)); EventManager.get().post(new MixToConfigChangedEvent(wallet));
}); });
} }
public MixConfig getMixConfig() {
return mixConfig;
}
} }

View file

@ -1,10 +1,11 @@
package com.sparrowwallet.sparrow.wallet; package com.sparrowwallet.sparrow.wallet;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import com.sparrowwallet.drongo.wallet.MixConfig;
import com.sparrowwallet.drongo.wallet.Wallet; 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.event.WalletMixConfigChangedEvent; import com.sparrowwallet.sparrow.event.MixToConfigChangedEvent;
import com.sparrowwallet.sparrow.whirlpool.Whirlpool; import com.sparrowwallet.sparrow.whirlpool.Whirlpool;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.scene.control.*; import javafx.scene.control.*;
@ -13,7 +14,7 @@ import org.controlsfx.tools.Borders;
import java.io.IOException; import java.io.IOException;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
public class MixToDialog extends Dialog<Boolean> { public class MixToDialog extends Dialog<MixConfig> {
private final Wallet wallet; private final Wallet wallet;
private final Button applyButton; private final Button applyButton;
@ -47,7 +48,7 @@ public class MixToDialog extends Dialog<Boolean> {
dialogPane.setPrefHeight(300); dialogPane.setPrefHeight(300);
AppServices.moveToActiveWindowScreen(this); AppServices.moveToActiveWindowScreen(this);
setResultConverter(dialogButton -> dialogButton == applyButtonType); setResultConverter(dialogButton -> dialogButton == applyButtonType ? mixToController.getMixConfig() : null);
setOnCloseRequest(event -> { setOnCloseRequest(event -> {
EventManager.get().unregister(this); EventManager.get().unregister(this);
@ -60,8 +61,8 @@ public class MixToDialog extends Dialog<Boolean> {
} }
@Subscribe @Subscribe
public void walletMixConfigChanged(WalletMixConfigChangedEvent event) { public void mixToConfigChanged(MixToConfigChangedEvent event) {
if(event.getWallet() == (wallet.isMasterWallet() ? wallet : wallet.getMasterWallet())) { if(event.getWallet() == wallet) {
applyButton.setDisable(false); applyButton.setDisable(false);
} }
} }

View file

@ -101,7 +101,7 @@ public class UtxosController extends WalletFormController implements Initializab
startMix.visibleProperty().bind(stopMix.visibleProperty().not()); startMix.visibleProperty().bind(stopMix.visibleProperty().not());
stopMix.visibleProperty().addListener((observable, oldValue, newValue) -> { stopMix.visibleProperty().addListener((observable, oldValue, newValue) -> {
stopMix.setDisable(!newValue); stopMix.setDisable(!newValue);
startMix.setDisable(newValue); startMix.setDisable(newValue || !AppServices.onlineProperty().get());
}); });
mixTo.managedProperty().bind(mixTo.visibleProperty()); mixTo.managedProperty().bind(mixTo.visibleProperty());
mixTo.setVisible(getWalletForm().getWallet().getStandardAccountType() == StandardAccount.WHIRLPOOL_POSTMIX); mixTo.setVisible(getWalletForm().getWallet().getStandardAccountType() == StandardAccount.WHIRLPOOL_POSTMIX);
@ -338,11 +338,17 @@ public class UtxosController extends WalletFormController implements Initializab
public void showMixToDialog(ActionEvent event) { public void showMixToDialog(ActionEvent event) {
MixToDialog mixToDialog = new MixToDialog(getWalletForm().getWallet()); MixToDialog mixToDialog = new MixToDialog(getWalletForm().getWallet());
Optional<Boolean> optApply = mixToDialog.showAndWait(); Optional<MixConfig> optMixConfig = mixToDialog.showAndWait();
if(optApply.isPresent() && optApply.get()) { if(optMixConfig.isPresent()) {
Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(getWalletForm().getWallet()); MixConfig changedMixConfig = optMixConfig.get();
MixConfig mixConfig = getWalletForm().getWallet().getMasterMixConfig(); MixConfig mixConfig = getWalletForm().getWallet().getMasterMixConfig();
mixConfig.setMixToWalletName(changedMixConfig.getMixToWalletName());
mixConfig.setMixToWalletFile(changedMixConfig.getMixToWalletFile());
mixConfig.setMinMixes(changedMixConfig.getMinMixes());
EventManager.get().post(new WalletMasterMixConfigChangedEvent(getWalletForm().getWallet()));
Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(getWalletForm().getWallet());
try { try {
String mixToWalletId = AppServices.getWhirlpoolServices().getWhirlpoolMixToWalletId(mixConfig); String mixToWalletId = AppServices.getWhirlpoolServices().getWhirlpoolMixToWalletId(mixConfig);
whirlpool.setMixToWallet(mixToWalletId, mixConfig.getMinMixes()); whirlpool.setMixToWallet(mixToWalletId, mixConfig.getMinMixes());

View file

@ -112,14 +112,10 @@ public class Whirlpool {
return poolSupplier.getPools(); return poolSupplier.getPools();
} }
public Tx0Preview getTx0Preview(Pool pool, Collection<UnspentOutput> utxos) throws Exception { public Tx0Previews getTx0Previews(Collection<UnspentOutput> utxos) throws Exception {
// preview all pools // preview all pools
Tx0Config tx0Config = computeTx0Config(); Tx0Config tx0Config = computeTx0Config();
Tx0Previews tx0Previews = tx0Service.tx0Previews(utxos, tx0Config); return tx0Service.tx0Previews(utxos, tx0Config);
// pool preview
String poolId = pool.getPoolId();
return tx0Previews.getTx0Preview(poolId);
} }
public Tx0 broadcastTx0(Pool pool, Collection<BlockTransactionHashIndex> utxos) throws Exception { public Tx0 broadcastTx0(Pool pool, Collection<BlockTransactionHashIndex> utxos) throws Exception {
@ -477,28 +473,26 @@ public class Whirlpool {
} }
} }
public static class Tx0PreviewService extends Service<Tx0Preview> { public static class Tx0PreviewsService extends Service<Tx0Previews> {
private final Whirlpool whirlpool; private final Whirlpool whirlpool;
private final Wallet wallet; private final Wallet wallet;
private final Pool pool;
private final List<UtxoEntry> utxoEntries; private final List<UtxoEntry> utxoEntries;
public Tx0PreviewService(Whirlpool whirlpool, Wallet wallet, Pool pool, List<UtxoEntry> utxoEntries) { public Tx0PreviewsService(Whirlpool whirlpool, Wallet wallet, List<UtxoEntry> utxoEntries) {
this.whirlpool = whirlpool; this.whirlpool = whirlpool;
this.wallet = wallet; this.wallet = wallet;
this.pool = pool;
this.utxoEntries = utxoEntries; this.utxoEntries = utxoEntries;
} }
@Override @Override
protected Task<Tx0Preview> createTask() { protected Task<Tx0Previews> createTask() {
return new Task<>() { return new Task<>() {
protected Tx0Preview call() throws Exception { protected Tx0Previews call() throws Exception {
updateProgress(-1, 1); updateProgress(-1, 1);
updateMessage("Fetching premix transaction..."); updateMessage("Fetching premix preview...");
Collection<UnspentOutput> utxos = utxoEntries.stream().map(utxoEntry -> Whirlpool.getUnspentOutput(wallet, utxoEntry.getNode(), utxoEntry.getBlockTransaction(), (int)utxoEntry.getHashIndex().getIndex())).collect(Collectors.toList()); Collection<UnspentOutput> utxos = utxoEntries.stream().map(utxoEntry -> Whirlpool.getUnspentOutput(wallet, utxoEntry.getNode(), utxoEntry.getBlockTransaction(), (int)utxoEntry.getHashIndex().getIndex())).collect(Collectors.toList());
return whirlpool.getTx0Preview(pool, utxos); return whirlpool.getTx0Previews(utxos);
} }
}; };
} }

View file

@ -1,6 +1,7 @@
package com.sparrowwallet.sparrow.whirlpool; 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.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;
@ -77,6 +78,7 @@ public class WhirlpoolController {
private Wallet wallet; private Wallet wallet;
private MixConfig mixConfig; private MixConfig mixConfig;
private List<UtxoEntry> utxoEntries; private List<UtxoEntry> utxoEntries;
private Tx0Previews tx0Previews;
private final ObjectProperty<Tx0Preview> tx0PreviewProperty = new SimpleObjectProperty<>(null); private final ObjectProperty<Tx0Preview> tx0PreviewProperty = new SimpleObjectProperty<>(null);
public void initializeView(String walletId, Wallet wallet, List<UtxoEntry> utxoEntries) { public void initializeView(String walletId, Wallet wallet, List<UtxoEntry> utxoEntries) {
@ -100,6 +102,8 @@ public class WhirlpoolController {
return change; return change;
})); }));
scode.textProperty().addListener((observable, oldValue, newValue) -> { scode.textProperty().addListener((observable, oldValue, newValue) -> {
pool.setItems(FXCollections.emptyObservableList());
tx0PreviewProperty.set(null);
mixConfig.setScode(newValue); mixConfig.setScode(newValue);
EventManager.get().post(new WalletMasterMixConfigChangedEvent(wallet)); EventManager.get().post(new WalletMasterMixConfigChangedEvent(wallet));
}); });
@ -151,6 +155,21 @@ public class WhirlpoolController {
nbOutputs.managedProperty().bind(nbOutputs.visibleProperty()); nbOutputs.managedProperty().bind(nbOutputs.visibleProperty());
nbOutputsLoading.visibleProperty().bind(nbOutputs.visibleProperty().not()); nbOutputsLoading.visibleProperty().bind(nbOutputs.visibleProperty().not());
nbOutputs.setVisible(false); nbOutputs.setVisible(false);
tx0PreviewProperty.addListener((observable, oldValue, tx0Preview) -> {
if(tx0Preview == null) {
nbOutputsBox.setVisible(true);
nbOutputsLoading.setText("Calculating...");
nbOutputs.setVisible(false);
discountFeeBox.setVisible(false);
} else {
discountFeeBox.setVisible(tx0Preview.getPool().getFeeValue() != tx0Preview.getTx0Data().getFeeValue());
discountFee.setValue(tx0Preview.getTx0Data().getFeeValue());
nbOutputsBox.setVisible(true);
nbOutputs.setText(tx0Preview.getNbPremix() + " UTXOs");
nbOutputs.setVisible(true);
}
});
} }
public boolean next() { public boolean next() {
@ -240,34 +259,36 @@ public class WhirlpoolController {
} }
Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(walletId); Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(walletId);
whirlpool.setScode(mixConfig.getScode()); if(tx0Previews != null && mixConfig.getScode().equals(whirlpool.getScode())) {
Tx0Preview tx0Preview = tx0Previews.getTx0Preview(pool.getPoolId());
Whirlpool.Tx0PreviewService tx0PreviewService = new Whirlpool.Tx0PreviewService(whirlpool, wallet, pool, utxoEntries);
tx0PreviewService.setOnRunning(workerStateEvent -> {
nbOutputsBox.setVisible(true);
nbOutputsLoading.setText("Calculating...");
nbOutputs.setVisible(false);
discountFeeBox.setVisible(false);
tx0PreviewProperty.set(null);
});
tx0PreviewService.setOnSucceeded(workerStateEvent -> {
Tx0Preview tx0Preview = tx0PreviewService.getValue();
discountFeeBox.setVisible(tx0Preview.getPool().getFeeValue() != tx0Preview.getTx0Data().getFeeValue());
discountFee.setValue(tx0Preview.getTx0Data().getFeeValue());
nbOutputsBox.setVisible(true);
nbOutputs.setText(tx0Preview.getNbPremix() + " UTXOs");
nbOutputs.setVisible(true);
tx0PreviewProperty.set(tx0Preview); tx0PreviewProperty.set(tx0Preview);
}); } else {
tx0PreviewService.setOnFailed(workerStateEvent -> { tx0Previews = null;
Throwable exception = workerStateEvent.getSource().getException(); whirlpool.setScode(mixConfig.getScode());
while(exception.getCause() != null) {
exception = exception.getCause();
}
nbOutputsLoading.setText("Error fetching fee: " + exception.getMessage()); Whirlpool.Tx0PreviewsService tx0PreviewsService = new Whirlpool.Tx0PreviewsService(whirlpool, wallet, utxoEntries);
}); tx0PreviewsService.setOnRunning(workerStateEvent -> {
tx0PreviewService.start(); nbOutputsBox.setVisible(true);
nbOutputsLoading.setText("Calculating...");
nbOutputs.setVisible(false);
discountFeeBox.setVisible(false);
tx0PreviewProperty.set(null);
});
tx0PreviewsService.setOnSucceeded(workerStateEvent -> {
tx0Previews = tx0PreviewsService.getValue();
Tx0Preview tx0Preview = tx0Previews.getTx0Preview(pool.getPoolId());
tx0PreviewProperty.set(tx0Preview);
});
tx0PreviewsService.setOnFailed(workerStateEvent -> {
Throwable exception = workerStateEvent.getSource().getException();
while(exception.getCause() != null) {
exception = exception.getCause();
}
nbOutputsLoading.setText("Error fetching fee: " + exception.getMessage());
});
tx0PreviewsService.start();
}
} }
public ObjectProperty<Tx0Preview> getTx0PreviewProperty() { public ObjectProperty<Tx0Preview> getTx0PreviewProperty() {