From 9ba4458f48aa76421a0ff83026a7648dd3c6f311 Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Tue, 2 Apr 2024 17:14:19 +0200 Subject: [PATCH] fix tor proxy switching for whirlpool client and mixing bip47 utxos --- build.gradle | 4 ++++ .../sparrowwallet/sparrow/AppServices.java | 5 +++++ .../sparrow/soroban/Soroban.java | 15 +++++++++---- .../sparrow/soroban/SorobanServices.java | 5 ++++- .../terminal/wallet/table/MixTableCell.java | 2 +- .../sparrow/whirlpool/Whirlpool.java | 16 +++++++++++++- .../sparrow/whirlpool/WhirlpoolServices.java | 21 +++++++++++++++++-- .../dataSource/SparrowChainSupplier.java | 3 +++ .../dataSource/SparrowDataSource.java | 1 + .../dataSource/SparrowUtxoSupplier.java | 3 ++- 10 files changed, 65 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index de1d1ee4..86ca0781 100644 --- a/build.gradle +++ b/build.gradle @@ -189,6 +189,8 @@ application { "--add-opens=java.base/java.net=com.sparrowwallet.sparrow", "--add-opens=java.base/java.io=com.google.gson", "--add-opens=java.smartcardio/sun.security.smartcardio=com.sparrowwallet.sparrow", + "--add-opens=com.samourai.whirlpool.client/com.samourai.whirlpool.client.whirlpool=com.sparrowwallet.sparrow", + "--add-opens=com.samourai.soroban.client/com.samourai.soroban.client.rpc=com.sparrowwallet.sparrow", "--add-reads=kotlin.stdlib=kotlinx.coroutines.core"] if(os.macOsX) { @@ -238,6 +240,8 @@ jlink { "--add-opens=java.base/java.net=com.sparrowwallet.sparrow", "--add-opens=java.base/java.io=com.google.gson", "--add-opens=java.smartcardio/sun.security.smartcardio=com.sparrowwallet.sparrow", + "--add-opens=com.samourai.whirlpool.client/com.samourai.whirlpool.client.whirlpool=com.sparrowwallet.sparrow", + "--add-opens=com.samourai.soroban.client/com.samourai.soroban.client.rpc=com.sparrowwallet.sparrow", "--add-reads=com.sparrowwallet.merged.module=java.desktop", "--add-reads=com.sparrowwallet.merged.module=java.sql", "--add-reads=com.sparrowwallet.merged.module=com.sparrowwallet.sparrow", diff --git a/src/main/java/com/sparrowwallet/sparrow/AppServices.java b/src/main/java/com/sparrowwallet/sparrow/AppServices.java index 84132b92..66c8e22a 100644 --- a/src/main/java/com/sparrowwallet/sparrow/AppServices.java +++ b/src/main/java/com/sparrowwallet/sparrow/AppServices.java @@ -167,6 +167,11 @@ public class AppServices { connectionService.cancel(); ratesService.cancel(); versionCheckService.cancel(); + + if(httpClientService != null) { + HttpClientService.ShutdownService shutdownService = new HttpClientService.ShutdownService(httpClientService); + shutdownService.start(); + } } } }; diff --git a/src/main/java/com/sparrowwallet/sparrow/soroban/Soroban.java b/src/main/java/com/sparrowwallet/sparrow/soroban/Soroban.java index 78ec0647..30df1525 100644 --- a/src/main/java/com/sparrowwallet/sparrow/soroban/Soroban.java +++ b/src/main/java/com/sparrowwallet/sparrow/soroban/Soroban.java @@ -2,7 +2,6 @@ package com.sparrowwallet.sparrow.soroban; import com.samourai.soroban.client.SorobanConfig; import com.samourai.soroban.client.wallet.SorobanWalletService; -import com.samourai.wallet.chain.ChainSupplier; import com.samourai.wallet.hd.HD_Wallet; import com.sparrowwallet.drongo.Network; import com.sparrowwallet.drongo.protocol.ScriptType; @@ -10,8 +9,6 @@ import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.whirlpool.Whirlpool; import com.sparrowwallet.sparrow.whirlpool.dataSource.SparrowChainSupplier; -import javafx.concurrent.Service; -import javafx.concurrent.Task; import org.bitcoinj.core.NetworkParameters; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,6 +25,7 @@ public class Soroban { private HD_Wallet hdWallet; private int bip47Account; + private SparrowChainSupplier chainSupplier; public Soroban() { SorobanConfig sorobanConfig = AppServices.getWhirlpoolServices().getSorobanConfig(); @@ -63,7 +61,10 @@ public class Soroban { } try { - ChainSupplier chainSupplier = new SparrowChainSupplier(wallet.getStoredBlockHeight()); + if(chainSupplier == null) { + chainSupplier = new SparrowChainSupplier(wallet.getStoredBlockHeight()); + chainSupplier.open(); + } return new SparrowCahootsWallet(chainSupplier, wallet, hdWallet, bip47Account); } catch(Exception e) { log.error("Could not create cahoots wallet", e); @@ -79,4 +80,10 @@ public class Soroban { public SorobanWalletService getSorobanWalletService() { return sorobanWalletService; } + + public void close() { + if(chainSupplier != null) { + chainSupplier.close(); + } + } } diff --git a/src/main/java/com/sparrowwallet/sparrow/soroban/SorobanServices.java b/src/main/java/com/sparrowwallet/sparrow/soroban/SorobanServices.java index 6c45debf..ae8384b0 100644 --- a/src/main/java/com/sparrowwallet/sparrow/soroban/SorobanServices.java +++ b/src/main/java/com/sparrowwallet/sparrow/soroban/SorobanServices.java @@ -53,7 +53,10 @@ public class SorobanServices { public void walletTabsClosed(WalletTabsClosedEvent event) { for(WalletTabData walletTabData : event.getClosedWalletTabData()) { String walletId = walletTabData.getStorage().getWalletId(walletTabData.getWallet()); - sorobanMap.remove(walletId); + Soroban soroban = sorobanMap.remove(walletId); + if(soroban != null) { + soroban.close(); + } } } } \ No newline at end of file diff --git a/src/main/java/com/sparrowwallet/sparrow/terminal/wallet/table/MixTableCell.java b/src/main/java/com/sparrowwallet/sparrow/terminal/wallet/table/MixTableCell.java index 49077588..4bcc84e7 100644 --- a/src/main/java/com/sparrowwallet/sparrow/terminal/wallet/table/MixTableCell.java +++ b/src/main/java/com/sparrowwallet/sparrow/terminal/wallet/table/MixTableCell.java @@ -43,7 +43,7 @@ public class MixTableCell extends TableCell { private String getMixFail(UtxoEntry.MixStatus mixStatus) { long elapsed = mixStatus.getMixErrorTimestamp() == null ? 0L : System.currentTimeMillis() - mixStatus.getMixErrorTimestamp(); - if(mixStatus.getMixFailReason() == MixFailReason.STOP_UTXO || !mixStatus.getMixFailReason().isError() || elapsed >= ERROR_DISPLAY_MILLIS) { + if(!mixStatus.getMixFailReason().isError() || elapsed >= ERROR_DISPLAY_MILLIS) { return getMixCountOnly(mixStatus); } diff --git a/src/main/java/com/sparrowwallet/sparrow/whirlpool/Whirlpool.java b/src/main/java/com/sparrowwallet/sparrow/whirlpool/Whirlpool.java index dec18890..4c5609c5 100644 --- a/src/main/java/com/sparrowwallet/sparrow/whirlpool/Whirlpool.java +++ b/src/main/java/com/sparrowwallet/sparrow/whirlpool/Whirlpool.java @@ -29,6 +29,7 @@ import com.samourai.whirlpool.client.wallet.data.dataPersister.DataPersisterFact import com.samourai.whirlpool.client.wallet.data.dataSource.DataSourceConfig; import com.samourai.whirlpool.client.wallet.data.dataSource.DataSourceFactory; import com.samourai.whirlpool.client.wallet.data.utxo.UtxoSupplier; +import com.samourai.whirlpool.client.whirlpool.WhirlpoolClientConfig; import com.samourai.whirlpool.client.whirlpool.beans.Pool; import com.sparrowwallet.drongo.ExtendedKey; import com.sparrowwallet.drongo.KeyPurpose; @@ -60,6 +61,7 @@ import org.bitcoinj.core.NetworkParameters; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.lang.reflect.Field; import java.util.*; import java.util.stream.Collectors; @@ -110,6 +112,18 @@ public class Whirlpool { return whirlpoolWalletConfig; } + void setOnion(boolean onion) { + if(config.isTorOnionCoordinator() ^ onion) { + try { + Field torField = WhirlpoolClientConfig.class.getDeclaredField("torOnionCoordinator"); + torField.setAccessible(true); + torField.set(config, onion); + } catch(Exception e) { + log.warn("Error changing onion on WhirlpoolWalletConfig", e); + } + } + } + private DataSourceConfig computeDataSourceConfig(Integer storedBlockHeight) { return new DataSourceConfig(SparrowMinerFeeSupplier.getInstance(), new SparrowChainSupplier(storedBlockHeight), BIP_FORMAT.PROVIDER, BIP_WALLETS.WHIRLPOOL); } @@ -469,7 +483,7 @@ public class Whirlpool { UnspentOutput.Xpub xpub = new UnspentOutput.Xpub(); ExtendedKey.Header header = testnet ? ExtendedKey.Header.tpub : ExtendedKey.Header.xpub; xpub.m = wallet.getKeystores().get(0).getExtendedPublicKey().toString(header); - xpub.path = node.getDerivationPath().toUpperCase(Locale.ROOT); + xpub.path = node.getWallet().isBip47() ? null : node.getDerivationPath().toUpperCase(Locale.ROOT); out.xpub = xpub; diff --git a/src/main/java/com/sparrowwallet/sparrow/whirlpool/WhirlpoolServices.java b/src/main/java/com/sparrowwallet/sparrow/whirlpool/WhirlpoolServices.java index a4334f08..de5dd621 100644 --- a/src/main/java/com/sparrowwallet/sparrow/whirlpool/WhirlpoolServices.java +++ b/src/main/java/com/sparrowwallet/sparrow/whirlpool/WhirlpoolServices.java @@ -3,6 +3,7 @@ package com.sparrowwallet.sparrow.whirlpool; import com.google.common.eventbus.Subscribe; import com.google.common.net.HostAndPort; import com.samourai.soroban.client.SorobanConfig; +import com.samourai.soroban.client.rpc.RpcClientService; import com.samourai.wallet.constants.SamouraiNetwork; import com.samourai.wallet.util.ExtLibJConfig; import com.samourai.whirlpool.client.wallet.WhirlpoolEventService; @@ -29,11 +30,13 @@ import javafx.util.Duration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.lang.reflect.Field; import java.net.SocketTimeoutException; import java.util.*; import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; +import static com.sparrowwallet.sparrow.AppServices.getHttpClientService; import static com.sparrowwallet.sparrow.AppServices.getTorProxy; import static org.bitcoinj.crypto.MnemonicCode.SPARROW_FIX_NFKD_MNEMONIC; @@ -52,9 +55,9 @@ public class WhirlpoolServices { private ExtLibJConfig computeExtLibJConfig() { HttpClientService httpClientService = AppServices.getHttpClientService(); - boolean onion = (AppServices.getTorProxy() != null); + boolean onion = (getTorProxy() != null); SamouraiNetwork samouraiNetwork = getSamouraiNetwork(); - return new ExtLibJConfig(samouraiNetwork, onion, Drongo.getProvider(), httpClientService); + return new ExtLibJConfig(samouraiNetwork, onion, Drongo.getProvider(), httpClientService); } public SamouraiNetwork getSamouraiNetwork() { @@ -107,6 +110,8 @@ public class WhirlpoolServices { public void startWhirlpool(Wallet wallet, Whirlpool whirlpool, boolean notifyIfMixToMissing) { if(wallet.getMasterMixConfig().getMixOnStartup() != Boolean.FALSE) { + whirlpool.setOnion(sorobanConfig.getExtLibJConfig().isOnion()); + try { String mixToWalletId = getWhirlpoolMixToWalletId(wallet.getMasterMixConfig()); whirlpool.setMixToWallet(mixToWalletId, wallet.getMasterMixConfig().getMinMixes()); @@ -221,6 +226,18 @@ public class WhirlpoolServices { @Subscribe public void newConnection(ConnectionEvent event) { + ExtLibJConfig extLibJConfig = sorobanConfig.getExtLibJConfig(); + extLibJConfig.setOnion(getTorProxy() != null); + getHttpClientService(); //Ensure proxy is updated + + try { + Field onionField = RpcClientService.class.getDeclaredField("onion"); + onionField.setAccessible(true); + onionField.set(sorobanConfig.getRpcClientService(), getTorProxy() != null); + } catch(Exception e) { + log.warn("Error changing onion on RpcClientService", e); + } + startAllWhirlpool(); bindDebugAccelerator(); } diff --git a/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowChainSupplier.java b/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowChainSupplier.java index ec29da5c..ea46aa90 100644 --- a/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowChainSupplier.java +++ b/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowChainSupplier.java @@ -16,6 +16,9 @@ public class SparrowChainSupplier implements ChainSupplier { public SparrowChainSupplier(Integer storedBlockHeight) { this.storedBlockHeight = AppServices.getCurrentBlockHeight() == null ? (storedBlockHeight != null ? storedBlockHeight : 0) : AppServices.getCurrentBlockHeight(); this.latestBlock = computeLatestBlock(); + } + + public void open() { EventManager.get().register(this); } 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 a9dede63..698751cc 100644 --- a/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowDataSource.java +++ b/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowDataSource.java @@ -83,6 +83,7 @@ public class SparrowDataSource extends AbstractDataSource { public void open(CoordinatorSupplier coordinatorSupplier) throws Exception { super.open(coordinatorSupplier); EventManager.get().register(this); + ((SparrowChainSupplier)getDataSourceConfig().getChainSupplier()).open(); } @Override diff --git a/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowUtxoSupplier.java b/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowUtxoSupplier.java index c0799fdf..f4e1aa86 100644 --- a/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowUtxoSupplier.java +++ b/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowUtxoSupplier.java @@ -128,7 +128,8 @@ public class SparrowUtxoSupplier extends BasicUtxoSupplier { @Override public byte[] _getPrivKeyBip47(UnspentOutput utxo) throws Exception { - Wallet wallet = SparrowDataSource.getWallet(utxo.xpub.m); + BipWallet bipWallet = getWalletSupplier().getWalletByXPub(utxo.xpub.m); + Wallet wallet = SparrowDataSource.getWallet(bipWallet.getBipPub()); Map walletUtxos = wallet.getWalletUtxos(); WalletNode node = walletUtxos.entrySet().stream() .filter(entry -> entry.getKey().getHash().equals(Sha256Hash.wrap(utxo.tx_hash)) && entry.getKey().getIndex() == utxo.tx_output_n)