mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-25 05:06:45 +00:00
refactor to WhirlpoolServices
This commit is contained in:
parent
2fc551e35b
commit
e8af7c70bd
11 changed files with 205 additions and 169 deletions
|
@ -898,7 +898,7 @@ public class AppController implements Initializable {
|
||||||
|
|
||||||
if(wallet.isWhirlpoolMasterWallet()) {
|
if(wallet.isWhirlpoolMasterWallet()) {
|
||||||
String walletId = storage.getWalletId(wallet);
|
String walletId = storage.getWalletId(wallet);
|
||||||
Whirlpool whirlpool = AppServices.get().getWhirlpool(walletId);
|
Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(walletId);
|
||||||
whirlpool.setScode(wallet.getMasterMixConfig().getScode());
|
whirlpool.setScode(wallet.getMasterMixConfig().getScode());
|
||||||
whirlpool.setHDWallet(storage.getWalletId(wallet), copy);
|
whirlpool.setHDWallet(storage.getWalletId(wallet), copy);
|
||||||
}
|
}
|
||||||
|
@ -1187,7 +1187,7 @@ public class AppController implements Initializable {
|
||||||
tab.setContextMenu(getTabContextMenu(tab));
|
tab.setContextMenu(getTabContextMenu(tab));
|
||||||
tab.setClosable(true);
|
tab.setClosable(true);
|
||||||
tab.setOnCloseRequest(event -> {
|
tab.setOnCloseRequest(event -> {
|
||||||
if(AppServices.get().getWhirlpoolForMixToWallet(((WalletTabData)tab.getUserData()).getWalletForm().getWalletId()) != null) {
|
if(AppServices.getWhirlpoolServices().getWhirlpoolForMixToWallet(((WalletTabData)tab.getUserData()).getWalletForm().getWalletId()) != null) {
|
||||||
Optional<ButtonType> optType = AppServices.showWarningDialog("Close mix to wallet?", "This wallet has been configured as the final destination for mixes, and needs to be open for this to occur.\n\nAre you sure you want to close?", ButtonType.NO, ButtonType.YES);
|
Optional<ButtonType> optType = AppServices.showWarningDialog("Close mix to wallet?", "This wallet has been configured as the final destination for mixes, and needs to be open for this to occur.\n\nAre you sure you want to close?", ButtonType.NO, ButtonType.YES);
|
||||||
if(optType.isPresent() && optType.get() == ButtonType.NO) {
|
if(optType.isPresent() && optType.get() == ButtonType.NO) {
|
||||||
event.consume();
|
event.consume();
|
||||||
|
|
|
@ -11,14 +11,13 @@ import com.sparrowwallet.drongo.protocol.Transaction;
|
||||||
import com.sparrowwallet.drongo.psbt.PSBT;
|
import com.sparrowwallet.drongo.psbt.PSBT;
|
||||||
import com.sparrowwallet.drongo.uri.BitcoinURI;
|
import com.sparrowwallet.drongo.uri.BitcoinURI;
|
||||||
import com.sparrowwallet.drongo.wallet.KeystoreSource;
|
import com.sparrowwallet.drongo.wallet.KeystoreSource;
|
||||||
import com.sparrowwallet.drongo.wallet.MixConfig;
|
|
||||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
import com.sparrowwallet.sparrow.control.TextUtils;
|
import com.sparrowwallet.sparrow.control.TextUtils;
|
||||||
import com.sparrowwallet.sparrow.control.TrayManager;
|
import com.sparrowwallet.sparrow.control.TrayManager;
|
||||||
import com.sparrowwallet.sparrow.event.*;
|
import com.sparrowwallet.sparrow.event.*;
|
||||||
import com.sparrowwallet.sparrow.io.*;
|
import com.sparrowwallet.sparrow.io.*;
|
||||||
import com.sparrowwallet.sparrow.net.*;
|
import com.sparrowwallet.sparrow.net.*;
|
||||||
import com.sparrowwallet.sparrow.whirlpool.Whirlpool;
|
import com.sparrowwallet.sparrow.whirlpool.WhirlpoolServices;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
@ -75,6 +74,8 @@ public class AppServices {
|
||||||
|
|
||||||
private static AppServices INSTANCE;
|
private static AppServices INSTANCE;
|
||||||
|
|
||||||
|
private final WhirlpoolServices whirlpoolServices = new WhirlpoolServices();
|
||||||
|
|
||||||
private final MainApp application;
|
private final MainApp application;
|
||||||
|
|
||||||
private final Map<Window, List<WalletTabData>> walletWindows = new LinkedHashMap<>();
|
private final Map<Window, List<WalletTabData>> walletWindows = new LinkedHashMap<>();
|
||||||
|
@ -93,8 +94,6 @@ public class AppServices {
|
||||||
|
|
||||||
private TorService torService;
|
private TorService torService;
|
||||||
|
|
||||||
private final Map<String, Whirlpool> whirlpoolMap = new HashMap<>();
|
|
||||||
|
|
||||||
private static Integer currentBlockHeight;
|
private static Integer currentBlockHeight;
|
||||||
|
|
||||||
private static BlockHeader latestBlockHeader;
|
private static BlockHeader latestBlockHeader;
|
||||||
|
@ -143,6 +142,7 @@ public class AppServices {
|
||||||
public AppServices(MainApp application) {
|
public AppServices(MainApp application) {
|
||||||
this.application = application;
|
this.application = application;
|
||||||
EventManager.get().register(this);
|
EventManager.get().register(this);
|
||||||
|
EventManager.get().register(whirlpoolServices);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
|
@ -421,6 +421,10 @@ public class AppServices {
|
||||||
return INSTANCE;
|
return INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static WhirlpoolServices getWhirlpoolServices() {
|
||||||
|
return get().whirlpoolServices;
|
||||||
|
}
|
||||||
|
|
||||||
public static AppController newAppWindow(Stage stage) {
|
public static AppController newAppWindow(Stage stage) {
|
||||||
try {
|
try {
|
||||||
FXMLLoader appLoader = new FXMLLoader(AppServices.class.getResource("app.fxml"));
|
FXMLLoader appLoader = new FXMLLoader(AppServices.class.getResource("app.fxml"));
|
||||||
|
@ -453,80 +457,6 @@ public class AppServices {
|
||||||
return application;
|
return application;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Whirlpool getWhirlpool(Wallet wallet) {
|
|
||||||
Wallet masterWallet = wallet.isMasterWallet() ? wallet : wallet.getMasterWallet();
|
|
||||||
for(List<WalletTabData> walletTabDataList : walletWindows.values()) {
|
|
||||||
for(WalletTabData walletTabData : walletTabDataList) {
|
|
||||||
if(walletTabData.getWallet() == masterWallet) {
|
|
||||||
return whirlpoolMap.get(walletTabData.getWalletForm().getWalletId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Whirlpool getWhirlpool(String walletId) {
|
|
||||||
Whirlpool whirlpool = whirlpoolMap.get(walletId);
|
|
||||||
if(whirlpool == null) {
|
|
||||||
HostAndPort torProxy = AppServices.isTorRunning() ? HostAndPort.fromParts("localhost", TorService.PROXY_PORT) : (Config.get().getProxyServer() == null || Config.get().getProxyServer().isEmpty() || !Config.get().isUseProxy() ? null : HostAndPort.fromString(Config.get().getProxyServer()));
|
|
||||||
whirlpool = new Whirlpool(Network.get(), torProxy);
|
|
||||||
whirlpoolMap.put(walletId, whirlpool);
|
|
||||||
}
|
|
||||||
|
|
||||||
return whirlpool;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startAllWhirlpool() {
|
|
||||||
for(Map.Entry<String, Whirlpool> entry : whirlpoolMap.entrySet().stream().filter(entry -> entry.getValue().hasWallet() && !entry.getValue().isStarted()).collect(Collectors.toList())) {
|
|
||||||
Wallet wallet = getWallet(entry.getKey());
|
|
||||||
Whirlpool whirlpool = entry.getValue();
|
|
||||||
startWhirlpool(wallet, whirlpool);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startWhirlpool(Wallet wallet, Whirlpool whirlpool) {
|
|
||||||
if(wallet.getMasterMixConfig().getMixOnStartup() != Boolean.FALSE) {
|
|
||||||
try {
|
|
||||||
String mixToWalletId = getWhirlpoolMixToWalletId(wallet.getMasterMixConfig());
|
|
||||||
whirlpool.setMixToWallet(mixToWalletId, wallet.getMasterMixConfig().getMinMixes());
|
|
||||||
} catch(NoSuchElementException e) {
|
|
||||||
showWarningDialog("Mix to wallet not open", wallet.getName() + " is configured to mix to " + wallet.getMasterMixConfig().getMixToWalletName() + ", but this wallet is not open. Mix to wallets are required to be open to avoid address reuse.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Whirlpool.StartupService startupService = new Whirlpool.StartupService(whirlpool);
|
|
||||||
startupService.setOnFailed(workerStateEvent -> {
|
|
||||||
log.error("Failed to start whirlpool", workerStateEvent.getSource().getException());
|
|
||||||
});
|
|
||||||
startupService.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void shutdownAllWhirlpool() {
|
|
||||||
for(Whirlpool whirlpool : whirlpoolMap.values().stream().filter(Whirlpool::isStarted).collect(Collectors.toList())) {
|
|
||||||
Whirlpool.ShutdownService shutdownService = new Whirlpool.ShutdownService(whirlpool);
|
|
||||||
shutdownService.setOnFailed(workerStateEvent -> {
|
|
||||||
log.error("Failed to shutdown whirlpool", workerStateEvent.getSource().getException());
|
|
||||||
});
|
|
||||||
shutdownService.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getWhirlpoolMixToWalletId(MixConfig mixConfig) {
|
|
||||||
if(mixConfig == null || mixConfig.getMixToWalletFile() == null || mixConfig.getMixToWalletName() == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return getOpenWallets().entrySet().stream()
|
|
||||||
.filter(entry -> entry.getValue().getWalletFile().equals(mixConfig.getMixToWalletFile()) && entry.getKey().getName().equals(mixConfig.getMixToWalletName()))
|
|
||||||
.map(entry -> entry.getValue().getWalletId(entry.getKey()))
|
|
||||||
.findFirst().orElseThrow();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Whirlpool getWhirlpoolForMixToWallet(String walletId) {
|
|
||||||
return whirlpoolMap.values().stream().filter(whirlpool -> walletId.equals(whirlpool.getMixToWalletId())).findFirst().orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void minimizeStage(Stage stage) {
|
public void minimizeStage(Stage stage) {
|
||||||
if(trayManager == null) {
|
if(trayManager == null) {
|
||||||
trayManager = new TrayManager();
|
trayManager = new TrayManager();
|
||||||
|
@ -867,12 +797,6 @@ public class AppServices {
|
||||||
addMempoolRateSizes(event.getMempoolRateSizes());
|
addMempoolRateSizes(event.getMempoolRateSizes());
|
||||||
minimumRelayFeeRate = event.getMinimumRelayFeeRate();
|
minimumRelayFeeRate = event.getMinimumRelayFeeRate();
|
||||||
latestBlockHeader = event.getBlockHeader();
|
latestBlockHeader = event.getBlockHeader();
|
||||||
startAllWhirlpool();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
public void disconnection(DisconnectionEvent event) {
|
|
||||||
shutdownAllWhirlpool();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
|
@ -1001,71 +925,6 @@ public class AppServices {
|
||||||
restartBwt(event.getWallet());
|
restartBwt(event.getWallet());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
public void walletOpened(WalletOpenedEvent event) {
|
|
||||||
String walletId = event.getStorage().getWalletId(event.getWallet());
|
|
||||||
Whirlpool whirlpool = whirlpoolMap.get(walletId);
|
|
||||||
if(whirlpool != null && !whirlpool.isStarted() && isConnected()) {
|
|
||||||
startWhirlpool(event.getWallet(), whirlpool);
|
|
||||||
}
|
|
||||||
|
|
||||||
Whirlpool mixFromWhirlpool = whirlpoolMap.entrySet().stream().filter(entry -> event.getStorage().getWalletFile().equals(getWallet(entry.getKey()).getMasterMixConfig().getMixToWalletFile())).map(Map.Entry::getValue).findFirst().orElse(null);
|
|
||||||
if(mixFromWhirlpool != null) {
|
|
||||||
mixFromWhirlpool.setMixToWallet(walletId, 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
public void walletTabsClosed(WalletTabsClosedEvent event) {
|
|
||||||
for(WalletTabData walletTabData : event.getClosedWalletTabData()) {
|
|
||||||
String walletId = walletTabData.getStorage().getWalletId(walletTabData.getWallet());
|
|
||||||
Whirlpool whirlpool = whirlpoolMap.remove(walletId);
|
|
||||||
if(whirlpool != null) {
|
|
||||||
if(whirlpool.isStarted()) {
|
|
||||||
Whirlpool.ShutdownService shutdownService = new Whirlpool.ShutdownService(whirlpool);
|
|
||||||
shutdownService.setOnSucceeded(workerStateEvent -> {
|
|
||||||
WhirlpoolEventService.getInstance().unregister(whirlpool);
|
|
||||||
});
|
|
||||||
shutdownService.setOnFailed(workerStateEvent -> {
|
|
||||||
log.error("Failed to shutdown whirlpool", workerStateEvent.getSource().getException());
|
|
||||||
});
|
|
||||||
shutdownService.start();
|
|
||||||
} else {
|
|
||||||
//Ensure http clients are shutdown
|
|
||||||
whirlpool.shutdown();
|
|
||||||
WhirlpoolEventService.getInstance().unregister(whirlpool);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Whirlpool mixToWhirlpool = getWhirlpoolForMixToWallet(walletId);
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
public void walletHistoryChanged(WalletHistoryChangedEvent event) {
|
|
||||||
Whirlpool whirlpool = getWhirlpool(event.getWallet());
|
|
||||||
if(whirlpool != null) {
|
|
||||||
whirlpool.refreshUtxos();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void restartBwt(Wallet wallet) {
|
private void restartBwt(Wallet wallet) {
|
||||||
if(Config.get().getServerType() == ServerType.BITCOIN_CORE && isConnected() && wallet.isValid()) {
|
if(Config.get().getServerType() == ServerType.BITCOIN_CORE && isConnected() && wallet.isValid()) {
|
||||||
connectionService.cancel();
|
connectionService.cancel();
|
||||||
|
|
|
@ -123,7 +123,7 @@ public class MixStatusCell extends TreeTableCell<Entry, UtxoEntry.MixStatus> {
|
||||||
|
|
||||||
private static class MixStatusContextMenu extends ContextMenu {
|
private static class MixStatusContextMenu extends ContextMenu {
|
||||||
public MixStatusContextMenu(UtxoEntry utxoEntry, boolean isMixing) {
|
public MixStatusContextMenu(UtxoEntry utxoEntry, boolean isMixing) {
|
||||||
Whirlpool pool = AppServices.get().getWhirlpool(utxoEntry.getWallet());
|
Whirlpool pool = AppServices.getWhirlpoolServices().getWhirlpool(utxoEntry.getWallet());
|
||||||
if(isMixing) {
|
if(isMixing) {
|
||||||
MenuItem mixStop = new MenuItem("Stop Mixing");
|
MenuItem mixStop = new MenuItem("Stop Mixing");
|
||||||
if(pool != null) {
|
if(pool != null) {
|
||||||
|
@ -132,7 +132,7 @@ public class MixStatusCell extends TreeTableCell<Entry, UtxoEntry.MixStatus> {
|
||||||
mixStop.setGraphic(getStopGlyph());
|
mixStop.setGraphic(getStopGlyph());
|
||||||
mixStop.setOnAction(event -> {
|
mixStop.setOnAction(event -> {
|
||||||
hide();
|
hide();
|
||||||
Whirlpool whirlpool = AppServices.get().getWhirlpool(utxoEntry.getWallet());
|
Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(utxoEntry.getWallet());
|
||||||
if(whirlpool != null) {
|
if(whirlpool != null) {
|
||||||
try {
|
try {
|
||||||
whirlpool.mixStop(utxoEntry.getHashIndex());
|
whirlpool.mixStop(utxoEntry.getHashIndex());
|
||||||
|
@ -151,7 +151,7 @@ public class MixStatusCell extends TreeTableCell<Entry, UtxoEntry.MixStatus> {
|
||||||
mixNow.setGraphic(getMixGlyph());
|
mixNow.setGraphic(getMixGlyph());
|
||||||
mixNow.setOnAction(event -> {
|
mixNow.setOnAction(event -> {
|
||||||
hide();
|
hide();
|
||||||
Whirlpool whirlpool = AppServices.get().getWhirlpool(utxoEntry.getWallet());
|
Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(utxoEntry.getWallet());
|
||||||
if(whirlpool != null) {
|
if(whirlpool != null) {
|
||||||
try {
|
try {
|
||||||
whirlpool.mix(utxoEntry.getHashIndex());
|
whirlpool.mix(utxoEntry.getHashIndex());
|
||||||
|
|
|
@ -52,7 +52,7 @@ public class MixToController implements Initializable {
|
||||||
|
|
||||||
String mixToWalletId = null;
|
String mixToWalletId = null;
|
||||||
try {
|
try {
|
||||||
mixToWalletId = AppServices.get().getWhirlpoolMixToWalletId(mixConfig);
|
mixToWalletId = AppServices.getWhirlpoolServices().getWhirlpoolMixToWalletId(mixConfig);
|
||||||
} catch(NoSuchElementException e) {
|
} catch(NoSuchElementException e) {
|
||||||
//ignore, mix to wallet is not open
|
//ignore, mix to wallet is not open
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ public class MixToDialog extends Dialog<Boolean> {
|
||||||
MixToController mixToController = mixToLoader.getController();
|
MixToController mixToController = mixToLoader.getController();
|
||||||
mixToController.initializeView(wallet);
|
mixToController.initializeView(wallet);
|
||||||
|
|
||||||
Whirlpool whirlpool = AppServices.get().getWhirlpool(wallet);
|
Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(wallet);
|
||||||
final ButtonType closeButtonType = new javafx.scene.control.ButtonType("Close", ButtonBar.ButtonData.CANCEL_CLOSE);
|
final ButtonType closeButtonType = new javafx.scene.control.ButtonType("Close", ButtonBar.ButtonData.CANCEL_CLOSE);
|
||||||
final ButtonType applyButtonType = new javafx.scene.control.ButtonType(whirlpool.isStarted() ? "Restart Whirlpool" : "Apply", ButtonBar.ButtonData.APPLY);
|
final ButtonType applyButtonType = new javafx.scene.control.ButtonType(whirlpool.isStarted() ? "Restart Whirlpool" : "Apply", ButtonBar.ButtonData.APPLY);
|
||||||
dialogPane.getButtonTypes().addAll(closeButtonType, applyButtonType);
|
dialogPane.getButtonTypes().addAll(closeButtonType, applyButtonType);
|
||||||
|
@ -38,7 +38,7 @@ public class MixToDialog extends Dialog<Boolean> {
|
||||||
applyButton.setDefaultButton(true);
|
applyButton.setDefaultButton(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
AppServices.get().getWhirlpoolMixToWalletId(wallet.getMasterMixConfig());
|
AppServices.getWhirlpoolServices().getWhirlpoolMixToWalletId(wallet.getMasterMixConfig());
|
||||||
} catch(NoSuchElementException e) {
|
} catch(NoSuchElementException e) {
|
||||||
applyButton.setDisable(false);
|
applyButton.setDisable(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1123,7 +1123,7 @@ public class SendController extends WalletFormController implements Initializabl
|
||||||
}
|
}
|
||||||
|
|
||||||
//The WhirlpoolWallet has already been configured for the tx0 preview
|
//The WhirlpoolWallet has already been configured for the tx0 preview
|
||||||
Whirlpool whirlpool = AppServices.get().getWhirlpool(getWalletForm().getStorage().getWalletId(masterWallet));
|
Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(getWalletForm().getStorage().getWalletId(masterWallet));
|
||||||
Map<BlockTransactionHashIndex, WalletNode> utxos = walletTransactionProperty.get().getSelectedUtxos();
|
Map<BlockTransactionHashIndex, WalletNode> utxos = walletTransactionProperty.get().getSelectedUtxos();
|
||||||
Whirlpool.Tx0BroadcastService tx0BroadcastService = new Whirlpool.Tx0BroadcastService(whirlpool, whirlpoolProperty.get(), utxos.keySet());
|
Whirlpool.Tx0BroadcastService tx0BroadcastService = new Whirlpool.Tx0BroadcastService(whirlpool, whirlpoolProperty.get(), utxos.keySet());
|
||||||
tx0BroadcastService.setOnRunning(workerStateEvent -> {
|
tx0BroadcastService.setOnRunning(workerStateEvent -> {
|
||||||
|
|
|
@ -149,7 +149,7 @@ public class UtxoEntry extends HashIndexEntry {
|
||||||
return wallet.getUtxoMixData(getHashIndex());
|
return wallet.getUtxoMixData(getHashIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
Whirlpool whirlpool = AppServices.get().getWhirlpool(wallet);
|
Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(wallet);
|
||||||
if(whirlpool != null) {
|
if(whirlpool != null) {
|
||||||
UtxoMixData utxoMixData = whirlpool.getMixData(getHashIndex());
|
UtxoMixData utxoMixData = whirlpool.getMixData(getHashIndex());
|
||||||
if(utxoMixData != null) {
|
if(utxoMixData != null) {
|
||||||
|
|
|
@ -107,7 +107,7 @@ public class UtxosController extends WalletFormController implements Initializab
|
||||||
mixTo.setVisible(getWalletForm().getWallet().getStandardAccountType() == StandardAccount.WHIRLPOOL_POSTMIX);
|
mixTo.setVisible(getWalletForm().getWallet().getStandardAccountType() == StandardAccount.WHIRLPOOL_POSTMIX);
|
||||||
|
|
||||||
if(mixButtonsBox.isVisible()) {
|
if(mixButtonsBox.isVisible()) {
|
||||||
Whirlpool whirlpool = AppServices.get().getWhirlpool(getWalletForm().getWallet());
|
Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(getWalletForm().getWallet());
|
||||||
if(whirlpool != null) {
|
if(whirlpool != null) {
|
||||||
stopMix.visibleProperty().bind(whirlpool.mixingProperty());
|
stopMix.visibleProperty().bind(whirlpool.mixingProperty());
|
||||||
whirlpool.startingProperty().addListener(new WeakChangeListener<>(mixingStartingListener));
|
whirlpool.startingProperty().addListener(new WeakChangeListener<>(mixingStartingListener));
|
||||||
|
@ -166,7 +166,7 @@ public class UtxosController extends WalletFormController implements Initializab
|
||||||
if(mixConfig != null && mixConfig.getMixToWalletName() != null) {
|
if(mixConfig != null && mixConfig.getMixToWalletName() != null) {
|
||||||
mixTo.setText("Mix to " + mixConfig.getMixToWalletName());
|
mixTo.setText("Mix to " + mixConfig.getMixToWalletName());
|
||||||
try {
|
try {
|
||||||
AppServices.get().getWhirlpoolMixToWalletId(mixConfig);
|
AppServices.getWhirlpoolServices().getWhirlpoolMixToWalletId(mixConfig);
|
||||||
mixTo.setGraphic(getExternalGlyph());
|
mixTo.setGraphic(getExternalGlyph());
|
||||||
mixTo.setTooltip(new Tooltip("Mixing to " + mixConfig.getMixToWalletName() + " after at least " + (mixConfig.getMinMixes() == null ? Whirlpool.DEFAULT_MIXTO_MIN_MIXES : mixConfig.getMinMixes()) + " mixes"));
|
mixTo.setTooltip(new Tooltip("Mixing to " + mixConfig.getMixToWalletName() + " after at least " + (mixConfig.getMinMixes() == null ? Whirlpool.DEFAULT_MIXTO_MIN_MIXES : mixConfig.getMinMixes()) + " mixes"));
|
||||||
} catch(NoSuchElementException e) {
|
} catch(NoSuchElementException e) {
|
||||||
|
@ -248,7 +248,7 @@ public class UtxosController extends WalletFormController implements Initializab
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prepareWhirlpoolWallet(Wallet decryptedWallet) {
|
private void prepareWhirlpoolWallet(Wallet decryptedWallet) {
|
||||||
Whirlpool whirlpool = AppServices.get().getWhirlpool(getWalletForm().getWalletId());
|
Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(getWalletForm().getWalletId());
|
||||||
whirlpool.setScode(decryptedWallet.getMasterMixConfig().getScode());
|
whirlpool.setScode(decryptedWallet.getMasterMixConfig().getScode());
|
||||||
whirlpool.setHDWallet(getWalletForm().getWalletId(), decryptedWallet);
|
whirlpool.setHDWallet(getWalletForm().getWalletId(), decryptedWallet);
|
||||||
|
|
||||||
|
@ -311,7 +311,7 @@ public class UtxosController extends WalletFormController implements Initializab
|
||||||
startMix.setDisable(true);
|
startMix.setDisable(true);
|
||||||
stopMix.setDisable(false);
|
stopMix.setDisable(false);
|
||||||
|
|
||||||
Whirlpool whirlpool = AppServices.get().getWhirlpool(getWalletForm().getWallet());
|
Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(getWalletForm().getWallet());
|
||||||
if(whirlpool != null && !whirlpool.isStarted() && AppServices.isConnected()) {
|
if(whirlpool != null && !whirlpool.isStarted() && AppServices.isConnected()) {
|
||||||
Whirlpool.StartupService startupService = new Whirlpool.StartupService(whirlpool);
|
Whirlpool.StartupService startupService = new Whirlpool.StartupService(whirlpool);
|
||||||
startupService.setOnFailed(workerStateEvent -> {
|
startupService.setOnFailed(workerStateEvent -> {
|
||||||
|
@ -329,7 +329,7 @@ public class UtxosController extends WalletFormController implements Initializab
|
||||||
stopMix.setDisable(true);
|
stopMix.setDisable(true);
|
||||||
startMix.setDisable(false);
|
startMix.setDisable(false);
|
||||||
|
|
||||||
Whirlpool whirlpool = AppServices.get().getWhirlpool(getWalletForm().getWallet());
|
Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(getWalletForm().getWallet());
|
||||||
if(whirlpool.isStarted()) {
|
if(whirlpool.isStarted()) {
|
||||||
Whirlpool.ShutdownService shutdownService = new Whirlpool.ShutdownService(whirlpool);
|
Whirlpool.ShutdownService shutdownService = new Whirlpool.ShutdownService(whirlpool);
|
||||||
shutdownService.setOnFailed(workerStateEvent -> {
|
shutdownService.setOnFailed(workerStateEvent -> {
|
||||||
|
@ -350,11 +350,11 @@ public class UtxosController extends WalletFormController implements Initializab
|
||||||
MixToDialog mixToDialog = new MixToDialog(getWalletForm().getWallet());
|
MixToDialog mixToDialog = new MixToDialog(getWalletForm().getWallet());
|
||||||
Optional<Boolean> optApply = mixToDialog.showAndWait();
|
Optional<Boolean> optApply = mixToDialog.showAndWait();
|
||||||
if(optApply.isPresent() && optApply.get()) {
|
if(optApply.isPresent() && optApply.get()) {
|
||||||
Whirlpool whirlpool = AppServices.get().getWhirlpool(getWalletForm().getWallet());
|
Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(getWalletForm().getWallet());
|
||||||
MixConfig mixConfig = getWalletForm().getWallet().getMasterMixConfig();
|
MixConfig mixConfig = getWalletForm().getWallet().getMasterMixConfig();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String mixToWalletId = AppServices.get().getWhirlpoolMixToWalletId(mixConfig);
|
String mixToWalletId = AppServices.getWhirlpoolServices().getWhirlpoolMixToWalletId(mixConfig);
|
||||||
whirlpool.setMixToWallet(mixToWalletId, mixConfig.getMinMixes());
|
whirlpool.setMixToWallet(mixToWalletId, mixConfig.getMinMixes());
|
||||||
} catch(NoSuchElementException e) {
|
} catch(NoSuchElementException e) {
|
||||||
mixConfig.setMixToWalletName(null);
|
mixConfig.setMixToWalletName(null);
|
||||||
|
|
|
@ -39,7 +39,7 @@ public class WalletUtxosEntry extends Entry {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void retrieveMixProgress() {
|
protected void retrieveMixProgress() {
|
||||||
Whirlpool whirlpool = AppServices.get().getWhirlpool(getWallet());
|
Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(getWallet());
|
||||||
if(whirlpool != null) {
|
if(whirlpool != null) {
|
||||||
for(Entry entry : getChildren()) {
|
for(Entry entry : getChildren()) {
|
||||||
UtxoEntry utxoEntry = (UtxoEntry)entry;
|
UtxoEntry utxoEntry = (UtxoEntry)entry;
|
||||||
|
|
|
@ -194,7 +194,7 @@ public class WhirlpoolController {
|
||||||
|
|
||||||
private void fetchPools() {
|
private void fetchPools() {
|
||||||
long totalUtxoValue = utxoEntries.stream().mapToLong(Entry::getValue).sum();
|
long totalUtxoValue = utxoEntries.stream().mapToLong(Entry::getValue).sum();
|
||||||
Whirlpool.PoolsService poolsService = new Whirlpool.PoolsService(AppServices.get().getWhirlpool(walletId));
|
Whirlpool.PoolsService poolsService = new Whirlpool.PoolsService(AppServices.getWhirlpoolServices().getWhirlpool(walletId));
|
||||||
poolsService.setOnSucceeded(workerStateEvent -> {
|
poolsService.setOnSucceeded(workerStateEvent -> {
|
||||||
List<Pool> availablePools = poolsService.getValue().stream().filter(pool1 -> totalUtxoValue >= (pool1.getPremixValueMin() + pool1.getFeeValue())).toList();
|
List<Pool> availablePools = poolsService.getValue().stream().filter(pool1 -> totalUtxoValue >= (pool1.getPremixValueMin() + pool1.getFeeValue())).toList();
|
||||||
if(availablePools.isEmpty()) {
|
if(availablePools.isEmpty()) {
|
||||||
|
@ -235,7 +235,7 @@ public class WhirlpoolController {
|
||||||
EventManager.get().post(new WalletMixConfigChangedEvent(wallet));
|
EventManager.get().post(new WalletMixConfigChangedEvent(wallet));
|
||||||
}
|
}
|
||||||
|
|
||||||
Whirlpool whirlpool = AppServices.get().getWhirlpool(walletId);
|
Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(walletId);
|
||||||
whirlpool.setScode(mixConfig.getScode());
|
whirlpool.setScode(mixConfig.getScode());
|
||||||
|
|
||||||
Whirlpool.Tx0PreviewService tx0PreviewService = new Whirlpool.Tx0PreviewService(whirlpool, wallet, pool, utxoEntries);
|
Whirlpool.Tx0PreviewService tx0PreviewService = new Whirlpool.Tx0PreviewService(whirlpool, wallet, pool, utxoEntries);
|
||||||
|
|
|
@ -0,0 +1,177 @@
|
||||||
|
package com.sparrowwallet.sparrow.whirlpool;
|
||||||
|
|
||||||
|
import com.google.common.eventbus.Subscribe;
|
||||||
|
import com.google.common.net.HostAndPort;
|
||||||
|
import com.samourai.whirlpool.client.wallet.WhirlpoolEventService;
|
||||||
|
import com.sparrowwallet.drongo.Network;
|
||||||
|
import com.sparrowwallet.drongo.wallet.MixConfig;
|
||||||
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
|
import com.sparrowwallet.sparrow.AppServices;
|
||||||
|
import com.sparrowwallet.sparrow.WalletTabData;
|
||||||
|
import com.sparrowwallet.sparrow.event.*;
|
||||||
|
import com.sparrowwallet.sparrow.io.Config;
|
||||||
|
import com.sparrowwallet.sparrow.io.Storage;
|
||||||
|
import com.sparrowwallet.sparrow.net.TorService;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class WhirlpoolServices {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(WhirlpoolServices.class);
|
||||||
|
|
||||||
|
private final Map<String, Whirlpool> whirlpoolMap = new HashMap<>();
|
||||||
|
|
||||||
|
public Whirlpool getWhirlpool(Wallet wallet) {
|
||||||
|
Wallet masterWallet = wallet.isMasterWallet() ? wallet : wallet.getMasterWallet();
|
||||||
|
for(Map.Entry<Wallet, Storage> entry : AppServices.get().getOpenWallets().entrySet()) {
|
||||||
|
if(entry.getKey() == masterWallet) {
|
||||||
|
return whirlpoolMap.get(entry.getValue().getWalletId(entry.getKey()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Whirlpool getWhirlpool(String walletId) {
|
||||||
|
Whirlpool whirlpool = whirlpoolMap.get(walletId);
|
||||||
|
if(whirlpool == null) {
|
||||||
|
HostAndPort torProxy = AppServices.isTorRunning() ? HostAndPort.fromParts("localhost", TorService.PROXY_PORT) : (Config.get().getProxyServer() == null || Config.get().getProxyServer().isEmpty() || !Config.get().isUseProxy() ? null : HostAndPort.fromString(Config.get().getProxyServer()));
|
||||||
|
whirlpool = new Whirlpool(Network.get(), torProxy);
|
||||||
|
whirlpoolMap.put(walletId, whirlpool);
|
||||||
|
}
|
||||||
|
|
||||||
|
return whirlpool;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startAllWhirlpool() {
|
||||||
|
for(Map.Entry<String, Whirlpool> entry : whirlpoolMap.entrySet().stream().filter(entry -> entry.getValue().hasWallet() && !entry.getValue().isStarted()).collect(Collectors.toList())) {
|
||||||
|
Wallet wallet = AppServices.get().getWallet(entry.getKey());
|
||||||
|
Whirlpool whirlpool = entry.getValue();
|
||||||
|
startWhirlpool(wallet, whirlpool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startWhirlpool(Wallet wallet, Whirlpool whirlpool) {
|
||||||
|
if(wallet.getMasterMixConfig().getMixOnStartup() != Boolean.FALSE) {
|
||||||
|
try {
|
||||||
|
String mixToWalletId = getWhirlpoolMixToWalletId(wallet.getMasterMixConfig());
|
||||||
|
whirlpool.setMixToWallet(mixToWalletId, wallet.getMasterMixConfig().getMinMixes());
|
||||||
|
} catch(NoSuchElementException e) {
|
||||||
|
AppServices.showWarningDialog("Mix to wallet not open", wallet.getName() + " is configured to mix to " + wallet.getMasterMixConfig().getMixToWalletName() + ", but this wallet is not open. Mix to wallets are required to be open to avoid address reuse.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Whirlpool.StartupService startupService = new Whirlpool.StartupService(whirlpool);
|
||||||
|
startupService.setOnFailed(workerStateEvent -> {
|
||||||
|
log.error("Failed to start whirlpool", workerStateEvent.getSource().getException());
|
||||||
|
});
|
||||||
|
startupService.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shutdownAllWhirlpool() {
|
||||||
|
for(Whirlpool whirlpool : whirlpoolMap.values().stream().filter(Whirlpool::isStarted).collect(Collectors.toList())) {
|
||||||
|
Whirlpool.ShutdownService shutdownService = new Whirlpool.ShutdownService(whirlpool);
|
||||||
|
shutdownService.setOnFailed(workerStateEvent -> {
|
||||||
|
log.error("Failed to shutdown whirlpool", workerStateEvent.getSource().getException());
|
||||||
|
});
|
||||||
|
shutdownService.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWhirlpoolMixToWalletId(MixConfig mixConfig) {
|
||||||
|
if(mixConfig == null || mixConfig.getMixToWalletFile() == null || mixConfig.getMixToWalletName() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AppServices.get().getOpenWallets().entrySet().stream()
|
||||||
|
.filter(entry -> entry.getValue().getWalletFile().equals(mixConfig.getMixToWalletFile()) && entry.getKey().getName().equals(mixConfig.getMixToWalletName()))
|
||||||
|
.map(entry -> entry.getValue().getWalletId(entry.getKey()))
|
||||||
|
.findFirst().orElseThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Whirlpool getWhirlpoolForMixToWallet(String walletId) {
|
||||||
|
return whirlpoolMap.values().stream().filter(whirlpool -> walletId.equals(whirlpool.getMixToWalletId())).findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void newConnection(ConnectionEvent event) {
|
||||||
|
startAllWhirlpool();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void disconnection(DisconnectionEvent event) {
|
||||||
|
shutdownAllWhirlpool();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void walletOpened(WalletOpenedEvent event) {
|
||||||
|
String walletId = event.getStorage().getWalletId(event.getWallet());
|
||||||
|
Whirlpool whirlpool = whirlpoolMap.get(walletId);
|
||||||
|
if(whirlpool != null && !whirlpool.isStarted() && AppServices.isConnected()) {
|
||||||
|
startWhirlpool(event.getWallet(), whirlpool);
|
||||||
|
}
|
||||||
|
|
||||||
|
Whirlpool mixFromWhirlpool = whirlpoolMap.entrySet().stream()
|
||||||
|
.filter(entry -> event.getStorage().getWalletFile().equals(AppServices.get().getWallet(entry.getKey()).getMasterMixConfig().getMixToWalletFile()))
|
||||||
|
.map(Map.Entry::getValue).findFirst().orElse(null);
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void walletTabsClosed(WalletTabsClosedEvent event) {
|
||||||
|
for(WalletTabData walletTabData : event.getClosedWalletTabData()) {
|
||||||
|
String walletId = walletTabData.getStorage().getWalletId(walletTabData.getWallet());
|
||||||
|
Whirlpool whirlpool = whirlpoolMap.remove(walletId);
|
||||||
|
if(whirlpool != null) {
|
||||||
|
if(whirlpool.isStarted()) {
|
||||||
|
Whirlpool.ShutdownService shutdownService = new Whirlpool.ShutdownService(whirlpool);
|
||||||
|
shutdownService.setOnSucceeded(workerStateEvent -> {
|
||||||
|
WhirlpoolEventService.getInstance().unregister(whirlpool);
|
||||||
|
});
|
||||||
|
shutdownService.setOnFailed(workerStateEvent -> {
|
||||||
|
log.error("Failed to shutdown whirlpool", workerStateEvent.getSource().getException());
|
||||||
|
});
|
||||||
|
shutdownService.start();
|
||||||
|
} else {
|
||||||
|
//Ensure http clients are shutdown
|
||||||
|
whirlpool.shutdown();
|
||||||
|
WhirlpoolEventService.getInstance().unregister(whirlpool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Whirlpool mixToWhirlpool = getWhirlpoolForMixToWallet(walletId);
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void walletHistoryChanged(WalletHistoryChangedEvent event) {
|
||||||
|
Whirlpool whirlpool = getWhirlpool(event.getWallet());
|
||||||
|
if(whirlpool != null) {
|
||||||
|
whirlpool.refreshUtxos();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue