diff --git a/build.gradle b/build.gradle index 1c1d6651..efa03670 100644 --- a/build.gradle +++ b/build.gradle @@ -91,7 +91,7 @@ dependencies { implementation('org.slf4j:jul-to-slf4j:1.7.30') { exclude group: 'org.slf4j' } - implementation('com.sparrowwallet.nightjar:nightjar:0.2.17-SNAPSHOT') + implementation('com.sparrowwallet.nightjar:nightjar:0.2.18-SNAPSHOT') testImplementation('junit:junit:4.12') } @@ -449,7 +449,7 @@ extraJavaModuleInfo { module('cbor-0.9.jar', 'co.nstant.in.cbor', '0.9') { exports('co.nstant.in.cbor') } - module('nightjar-0.2.17-SNAPSHOT.jar', 'com.sparrowwallet.nightjar', '0.2.17-SNAPSHOT') { + module('nightjar-0.2.18-SNAPSHOT.jar', 'com.sparrowwallet.nightjar', '0.2.18-SNAPSHOT') { requires('com.google.common') requires('net.sourceforge.streamsupport') requires('org.slf4j') diff --git a/src/main/java/com/sparrowwallet/sparrow/control/MixStatusCell.java b/src/main/java/com/sparrowwallet/sparrow/control/MixStatusCell.java index c6140f1c..2ba076c8 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/MixStatusCell.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/MixStatusCell.java @@ -40,12 +40,6 @@ public class MixStatusCell extends TreeTableCell<Entry, UtxoEntry.MixStatus> { setContextMenu(null); } - if(mixStatus.getMixProgress() != null) { - Tooltip tooltip = new Tooltip(); - tooltip.setText("Pool: " + mixStatus.getMixProgress().getPoolId().replace("btc", " BTC")); - setTooltip(tooltip); - } - if(mixStatus.getNextMixUtxo() != null) { setMixSuccess(mixStatus.getNextMixUtxo()); } else if(mixStatus.getMixFailReason() != null) { @@ -54,6 +48,7 @@ public class MixStatusCell extends TreeTableCell<Entry, UtxoEntry.MixStatus> { setMixProgress(mixStatus.getMixProgress()); } else { setGraphic(null); + setTooltip(null); } } } @@ -75,6 +70,7 @@ public class MixStatusCell extends TreeTableCell<Entry, UtxoEntry.MixStatus> { setTooltip(tt); } else { setGraphic(null); + setTooltip(null); } } @@ -88,6 +84,7 @@ public class MixStatusCell extends TreeTableCell<Entry, UtxoEntry.MixStatus> { setTooltip(tt); } else { setGraphic(null); + setTooltip(null); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java b/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java index 4019ee94..6b52dfe7 100644 --- a/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java +++ b/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java @@ -705,7 +705,7 @@ public class ElectrumServer { } public void copyPostmixLabels(Wallet wallet, Set<BlockTransactionHashIndex> newTransactionOutputs) { - if(wallet.getStandardAccountType() == StandardAccount.WHIRLPOOL_POSTMIX) { + if(wallet.getStandardAccountType() == StandardAccount.WHIRLPOOL_POSTMIX && wallet.getMasterWallet() != null) { for(BlockTransactionHashIndex newRef : newTransactionOutputs) { BlockTransactionHashIndex prevRef = wallet.getWalletTxos().keySet().stream() .filter(txo -> wallet.getMasterWallet().getUtxoMixData(txo) != null && txo.isSpent() && txo.getSpentBy().getHash().equals(newRef.getHash())).findFirst().orElse(null); diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/UtxosController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/UtxosController.java index 2ddcdbc9..6d0d491e 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/UtxosController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/UtxosController.java @@ -92,6 +92,20 @@ public class UtxosController extends WalletFormController implements Initializab mixTo.setDisable(newValue); }; + private final ChangeListener<Boolean> mixingListener = (observable, oldValue, newValue) -> { + if(!newValue) { + WalletUtxosEntry walletUtxosEntry = getWalletForm().getWalletUtxosEntry(); + for(Entry entry : walletUtxosEntry.getChildren()) { + UtxoEntry utxoEntry = (UtxoEntry)entry; + if(utxoEntry.getMixStatus() != null && utxoEntry.getMixStatus().getMixProgress() != null + && utxoEntry.getMixStatus().getMixProgress().getMixStep() != null + && utxoEntry.getMixStatus().getMixProgress().getMixStep().isInterruptable()) { + utxoEntry.setMixProgress(null); + } + } + } + }; + @Override public void initialize(URL location, ResourceBundle resources) { EventManager.get().register(this); @@ -120,6 +134,7 @@ public class UtxosController extends WalletFormController implements Initializab stopMix.visibleProperty().bind(whirlpool.mixingProperty()); whirlpool.startingProperty().addListener(new WeakChangeListener<>(mixingStartingListener)); whirlpool.stoppingProperty().addListener(new WeakChangeListener<>(mixingStoppingListener)); + whirlpool.mixingProperty().addListener(new WeakChangeListener<>(mixingListener)); updateMixToButton(); } } @@ -363,11 +378,8 @@ public class UtxosController extends WalletFormController implements Initializab updateMixToButton(); if(whirlpool.isStarted()) { - Whirlpool.RestartService restartService = new Whirlpool.RestartService(whirlpool); - restartService.setOnFailed(workerStateEvent -> { - log.error("Failed to restart whirlpool", workerStateEvent.getSource().getException()); - }); - restartService.start(); + //Will automatically restart + AppServices.getWhirlpoolServices().stopWhirlpool(whirlpool, false); } } } diff --git a/src/main/java/com/sparrowwallet/sparrow/whirlpool/Whirlpool.java b/src/main/java/com/sparrowwallet/sparrow/whirlpool/Whirlpool.java index 0cdb8459..7286bccf 100644 --- a/src/main/java/com/sparrowwallet/sparrow/whirlpool/Whirlpool.java +++ b/src/main/java/com/sparrowwallet/sparrow/whirlpool/Whirlpool.java @@ -45,6 +45,7 @@ import com.sparrowwallet.sparrow.whirlpool.dataSource.SparrowPostmixHandler; import javafx.application.Platform; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; +import javafx.concurrent.ScheduledService; import javafx.concurrent.Service; import javafx.concurrent.Task; import org.slf4j.Logger; @@ -467,6 +468,15 @@ public class Whirlpool { public void onWalletStop(WalletStopEvent e) { if(e.getWhirlpoolWallet() == whirlpoolWalletService.whirlpoolWallet()) { mixingProperty.set(false); + + Wallet wallet = AppServices.get().getWallet(walletId); + if(wallet != null) { + Platform.runLater(() -> { + if(AppServices.isConnected()) { + AppServices.getWhirlpoolServices().startWhirlpool(wallet, this, false); + } + }); + } } } @@ -539,7 +549,7 @@ public class Whirlpool { } } - public static class StartupService extends Service<WhirlpoolWallet> { + public static class StartupService extends ScheduledService<WhirlpoolWallet> { private final Whirlpool whirlpool; public StartupService(Whirlpool whirlpool) { @@ -588,37 +598,6 @@ public class Whirlpool { } } - public static class RestartService extends Service<Boolean> { - private final Whirlpool whirlpool; - - public RestartService(Whirlpool whirlpool) { - this.whirlpool = whirlpool; - } - - @Override - protected Task<Boolean> createTask() { - return new Task<>() { - protected Boolean call() throws Exception { - updateProgress(-1, 1); - updateMessage("Disconnecting from Whirlpool..."); - whirlpool.stoppingProperty.set(true); - whirlpool.shutdown(); - whirlpool.stoppingProperty.set(false); - - updateMessage("Starting Whirlpool..."); - whirlpool.startingProperty.set(true); - WhirlpoolWallet whirlpoolWallet = whirlpool.getWhirlpoolWallet(); - if(AppServices.onlineProperty().get()) { - whirlpoolWallet.start(); - } - whirlpool.startingProperty.set(false); - - return true; - } - }; - } - } - public static class WalletUtxo { public final Wallet wallet; public final BlockTransactionHashIndex utxo; diff --git a/src/main/java/com/sparrowwallet/sparrow/whirlpool/WhirlpoolServices.java b/src/main/java/com/sparrowwallet/sparrow/whirlpool/WhirlpoolServices.java index b13e0fb2..937e6602 100644 --- a/src/main/java/com/sparrowwallet/sparrow/whirlpool/WhirlpoolServices.java +++ b/src/main/java/com/sparrowwallet/sparrow/whirlpool/WhirlpoolServices.java @@ -15,6 +15,7 @@ import com.sparrowwallet.sparrow.event.*; import com.sparrowwallet.sparrow.io.Config; import com.sparrowwallet.sparrow.io.Storage; import com.sparrowwallet.sparrow.net.TorService; +import javafx.util.Duration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,6 +29,7 @@ public class WhirlpoolServices { private static final Logger log = LoggerFactory.getLogger(WhirlpoolServices.class); private final Map<String, Whirlpool> whirlpoolMap = new HashMap<>(); + private Whirlpool.StartupService startupService; public Whirlpool getWhirlpool(Wallet wallet) { Wallet masterWallet = wallet.isMasterWallet() ? wallet : wallet.getMasterWallet(); @@ -86,7 +88,15 @@ public class WhirlpoolServices { } } - Whirlpool.StartupService startupService = new Whirlpool.StartupService(whirlpool); + if(startupService != null) { + startupService.cancel(); + } + + startupService = new Whirlpool.StartupService(whirlpool); + startupService.setPeriod(Duration.minutes(2)); + startupService.setOnSucceeded(workerStateEvent -> { + startupService.cancel(); + }); startupService.setOnFailed(workerStateEvent -> { log.error("Failed to start whirlpool", workerStateEvent.getSource().getException()); }); @@ -175,11 +185,8 @@ public class WhirlpoolServices { if(mixFromWhirlpool != null) { mixFromWhirlpool.setMixToWallet(walletId, AppServices.get().getWallet(mixFromWhirlpool.getWalletId()).getMasterMixConfig().getMinMixes()); if(mixFromWhirlpool.isStarted()) { - Whirlpool.RestartService restartService = new Whirlpool.RestartService(mixFromWhirlpool); - restartService.setOnFailed(workerStateEvent -> { - log.error("Failed to restart whirlpool", workerStateEvent.getSource().getException()); - }); - restartService.start(); + //Will automatically restart + stopWhirlpool(mixFromWhirlpool, false); } } } @@ -210,11 +217,8 @@ public class WhirlpoolServices { if(mixToWhirlpool != null && event.getClosedWalletTabData().stream().noneMatch(walletTabData1 -> walletTabData1.getWalletForm().getWalletId().equals(mixToWhirlpool.getWalletId()))) { mixToWhirlpool.setMixToWallet(null, null); if(mixToWhirlpool.isStarted()) { - Whirlpool.RestartService restartService = new Whirlpool.RestartService(mixToWhirlpool); - restartService.setOnFailed(workerStateEvent -> { - log.error("Failed to restart whirlpool", workerStateEvent.getSource().getException()); - }); - restartService.start(); + //Will automatically restart + stopWhirlpool(mixToWhirlpool, false); } } } diff --git a/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowDataSource.java b/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowDataSource.java index 8aea2f66..42e28182 100644 --- a/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowDataSource.java +++ b/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowDataSource.java @@ -18,10 +18,7 @@ import com.sparrowwallet.drongo.protocol.Sha256Hash; import com.sparrowwallet.drongo.protocol.Transaction; import com.sparrowwallet.drongo.protocol.TransactionInput; import com.sparrowwallet.drongo.protocol.TransactionOutput; -import com.sparrowwallet.drongo.wallet.BlockTransaction; -import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex; -import com.sparrowwallet.drongo.wallet.Wallet; -import com.sparrowwallet.drongo.wallet.WalletNode; +import com.sparrowwallet.drongo.wallet.*; import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.event.NewBlockEvent; @@ -99,7 +96,7 @@ public class SparrowDataSource extends WalletResponseDataSource { for(Map.Entry<BlockTransactionHashIndex, WalletNode> utxo : wallet.getWalletUtxos().entrySet()) { BlockTransaction blockTransaction = wallet.getTransactions().get(utxo.getKey().getHash()); - if(blockTransaction != null) { + if(blockTransaction != null && utxo.getKey().getStatus() != Status.FROZEN) { unspentOutputs.add(Whirlpool.getUnspentOutput(wallet, utxo.getValue(), blockTransaction, (int)utxo.getKey().getIndex())); } } @@ -179,13 +176,13 @@ public class SparrowDataSource extends WalletResponseDataSource { static Wallet getWallet(String zpub) { return AppServices.get().getOpenWallets().keySet().stream() - .filter(Wallet::isValid) .filter(wallet -> { List<ExtendedKey.Header> headers = ExtendedKey.Header.getHeaders(Network.get()); ExtendedKey.Header header = headers.stream().filter(head -> head.getDefaultScriptType().equals(wallet.getScriptType()) && !head.isPrivateKey()).findFirst().orElse(ExtendedKey.Header.xpub); ExtendedKey extPubKey = wallet.getKeystores().get(0).getExtendedPublicKey(); return extPubKey.toString(header).equals(zpub); }) + .filter(Wallet::isValid) .findFirst() .orElse(null); }