Craig Raw
3 years ago
11 changed files with 205 additions and 169 deletions
@ -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 new issue