fix tor proxy switching for whirlpool client and mixing bip47 utxos

This commit is contained in:
Craig Raw 2024-04-02 17:14:19 +02:00
parent a9fd7c263f
commit 9ba4458f48
10 changed files with 65 additions and 10 deletions

View file

@ -189,6 +189,8 @@ application {
"--add-opens=java.base/java.net=com.sparrowwallet.sparrow", "--add-opens=java.base/java.net=com.sparrowwallet.sparrow",
"--add-opens=java.base/java.io=com.google.gson", "--add-opens=java.base/java.io=com.google.gson",
"--add-opens=java.smartcardio/sun.security.smartcardio=com.sparrowwallet.sparrow", "--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"] "--add-reads=kotlin.stdlib=kotlinx.coroutines.core"]
if(os.macOsX) { if(os.macOsX) {
@ -238,6 +240,8 @@ jlink {
"--add-opens=java.base/java.net=com.sparrowwallet.sparrow", "--add-opens=java.base/java.net=com.sparrowwallet.sparrow",
"--add-opens=java.base/java.io=com.google.gson", "--add-opens=java.base/java.io=com.google.gson",
"--add-opens=java.smartcardio/sun.security.smartcardio=com.sparrowwallet.sparrow", "--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.desktop",
"--add-reads=com.sparrowwallet.merged.module=java.sql", "--add-reads=com.sparrowwallet.merged.module=java.sql",
"--add-reads=com.sparrowwallet.merged.module=com.sparrowwallet.sparrow", "--add-reads=com.sparrowwallet.merged.module=com.sparrowwallet.sparrow",

View file

@ -167,6 +167,11 @@ public class AppServices {
connectionService.cancel(); connectionService.cancel();
ratesService.cancel(); ratesService.cancel();
versionCheckService.cancel(); versionCheckService.cancel();
if(httpClientService != null) {
HttpClientService.ShutdownService shutdownService = new HttpClientService.ShutdownService(httpClientService);
shutdownService.start();
}
} }
} }
}; };

View file

@ -2,7 +2,6 @@ package com.sparrowwallet.sparrow.soroban;
import com.samourai.soroban.client.SorobanConfig; import com.samourai.soroban.client.SorobanConfig;
import com.samourai.soroban.client.wallet.SorobanWalletService; import com.samourai.soroban.client.wallet.SorobanWalletService;
import com.samourai.wallet.chain.ChainSupplier;
import com.samourai.wallet.hd.HD_Wallet; import com.samourai.wallet.hd.HD_Wallet;
import com.sparrowwallet.drongo.Network; import com.sparrowwallet.drongo.Network;
import com.sparrowwallet.drongo.protocol.ScriptType; 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.AppServices;
import com.sparrowwallet.sparrow.whirlpool.Whirlpool; import com.sparrowwallet.sparrow.whirlpool.Whirlpool;
import com.sparrowwallet.sparrow.whirlpool.dataSource.SparrowChainSupplier; import com.sparrowwallet.sparrow.whirlpool.dataSource.SparrowChainSupplier;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.core.NetworkParameters;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -28,6 +25,7 @@ public class Soroban {
private HD_Wallet hdWallet; private HD_Wallet hdWallet;
private int bip47Account; private int bip47Account;
private SparrowChainSupplier chainSupplier;
public Soroban() { public Soroban() {
SorobanConfig sorobanConfig = AppServices.getWhirlpoolServices().getSorobanConfig(); SorobanConfig sorobanConfig = AppServices.getWhirlpoolServices().getSorobanConfig();
@ -63,7 +61,10 @@ public class Soroban {
} }
try { try {
ChainSupplier chainSupplier = new SparrowChainSupplier(wallet.getStoredBlockHeight()); if(chainSupplier == null) {
chainSupplier = new SparrowChainSupplier(wallet.getStoredBlockHeight());
chainSupplier.open();
}
return new SparrowCahootsWallet(chainSupplier, wallet, hdWallet, bip47Account); return new SparrowCahootsWallet(chainSupplier, wallet, hdWallet, bip47Account);
} catch(Exception e) { } catch(Exception e) {
log.error("Could not create cahoots wallet", e); log.error("Could not create cahoots wallet", e);
@ -79,4 +80,10 @@ public class Soroban {
public SorobanWalletService getSorobanWalletService() { public SorobanWalletService getSorobanWalletService() {
return sorobanWalletService; return sorobanWalletService;
} }
public void close() {
if(chainSupplier != null) {
chainSupplier.close();
}
}
} }

View file

@ -53,7 +53,10 @@ public class SorobanServices {
public void walletTabsClosed(WalletTabsClosedEvent event) { public void walletTabsClosed(WalletTabsClosedEvent event) {
for(WalletTabData walletTabData : event.getClosedWalletTabData()) { for(WalletTabData walletTabData : event.getClosedWalletTabData()) {
String walletId = walletTabData.getStorage().getWalletId(walletTabData.getWallet()); String walletId = walletTabData.getStorage().getWalletId(walletTabData.getWallet());
sorobanMap.remove(walletId); Soroban soroban = sorobanMap.remove(walletId);
if(soroban != null) {
soroban.close();
}
} }
} }
} }

View file

@ -43,7 +43,7 @@ public class MixTableCell extends TableCell {
private String getMixFail(UtxoEntry.MixStatus mixStatus) { private String getMixFail(UtxoEntry.MixStatus mixStatus) {
long elapsed = mixStatus.getMixErrorTimestamp() == null ? 0L : System.currentTimeMillis() - mixStatus.getMixErrorTimestamp(); 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); return getMixCountOnly(mixStatus);
} }

View file

@ -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.DataSourceConfig;
import com.samourai.whirlpool.client.wallet.data.dataSource.DataSourceFactory; import com.samourai.whirlpool.client.wallet.data.dataSource.DataSourceFactory;
import com.samourai.whirlpool.client.wallet.data.utxo.UtxoSupplier; 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.samourai.whirlpool.client.whirlpool.beans.Pool;
import com.sparrowwallet.drongo.ExtendedKey; import com.sparrowwallet.drongo.ExtendedKey;
import com.sparrowwallet.drongo.KeyPurpose; import com.sparrowwallet.drongo.KeyPurpose;
@ -60,6 +61,7 @@ import org.bitcoinj.core.NetworkParameters;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.lang.reflect.Field;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -110,6 +112,18 @@ public class Whirlpool {
return whirlpoolWalletConfig; 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) { private DataSourceConfig computeDataSourceConfig(Integer storedBlockHeight) {
return new DataSourceConfig(SparrowMinerFeeSupplier.getInstance(), new SparrowChainSupplier(storedBlockHeight), BIP_FORMAT.PROVIDER, BIP_WALLETS.WHIRLPOOL); 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(); UnspentOutput.Xpub xpub = new UnspentOutput.Xpub();
ExtendedKey.Header header = testnet ? ExtendedKey.Header.tpub : ExtendedKey.Header.xpub; ExtendedKey.Header header = testnet ? ExtendedKey.Header.tpub : ExtendedKey.Header.xpub;
xpub.m = wallet.getKeystores().get(0).getExtendedPublicKey().toString(header); 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; out.xpub = xpub;

View file

@ -3,6 +3,7 @@ package com.sparrowwallet.sparrow.whirlpool;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import com.google.common.net.HostAndPort; import com.google.common.net.HostAndPort;
import com.samourai.soroban.client.SorobanConfig; import com.samourai.soroban.client.SorobanConfig;
import com.samourai.soroban.client.rpc.RpcClientService;
import com.samourai.wallet.constants.SamouraiNetwork; import com.samourai.wallet.constants.SamouraiNetwork;
import com.samourai.wallet.util.ExtLibJConfig; import com.samourai.wallet.util.ExtLibJConfig;
import com.samourai.whirlpool.client.wallet.WhirlpoolEventService; import com.samourai.whirlpool.client.wallet.WhirlpoolEventService;
@ -29,11 +30,13 @@ import javafx.util.Duration;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.lang.reflect.Field;
import java.net.SocketTimeoutException; import java.net.SocketTimeoutException;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.sparrowwallet.sparrow.AppServices.getHttpClientService;
import static com.sparrowwallet.sparrow.AppServices.getTorProxy; import static com.sparrowwallet.sparrow.AppServices.getTorProxy;
import static org.bitcoinj.crypto.MnemonicCode.SPARROW_FIX_NFKD_MNEMONIC; import static org.bitcoinj.crypto.MnemonicCode.SPARROW_FIX_NFKD_MNEMONIC;
@ -52,7 +55,7 @@ public class WhirlpoolServices {
private ExtLibJConfig computeExtLibJConfig() { private ExtLibJConfig computeExtLibJConfig() {
HttpClientService httpClientService = AppServices.getHttpClientService(); HttpClientService httpClientService = AppServices.getHttpClientService();
boolean onion = (AppServices.getTorProxy() != null); boolean onion = (getTorProxy() != null);
SamouraiNetwork samouraiNetwork = getSamouraiNetwork(); SamouraiNetwork samouraiNetwork = getSamouraiNetwork();
return new ExtLibJConfig(samouraiNetwork, onion, Drongo.getProvider(), httpClientService); return new ExtLibJConfig(samouraiNetwork, onion, Drongo.getProvider(), httpClientService);
} }
@ -107,6 +110,8 @@ public class WhirlpoolServices {
public void startWhirlpool(Wallet wallet, Whirlpool whirlpool, boolean notifyIfMixToMissing) { public void startWhirlpool(Wallet wallet, Whirlpool whirlpool, boolean notifyIfMixToMissing) {
if(wallet.getMasterMixConfig().getMixOnStartup() != Boolean.FALSE) { if(wallet.getMasterMixConfig().getMixOnStartup() != Boolean.FALSE) {
whirlpool.setOnion(sorobanConfig.getExtLibJConfig().isOnion());
try { try {
String mixToWalletId = getWhirlpoolMixToWalletId(wallet.getMasterMixConfig()); String mixToWalletId = getWhirlpoolMixToWalletId(wallet.getMasterMixConfig());
whirlpool.setMixToWallet(mixToWalletId, wallet.getMasterMixConfig().getMinMixes()); whirlpool.setMixToWallet(mixToWalletId, wallet.getMasterMixConfig().getMinMixes());
@ -221,6 +226,18 @@ public class WhirlpoolServices {
@Subscribe @Subscribe
public void newConnection(ConnectionEvent event) { 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(); startAllWhirlpool();
bindDebugAccelerator(); bindDebugAccelerator();
} }

View file

@ -16,6 +16,9 @@ public class SparrowChainSupplier implements ChainSupplier {
public SparrowChainSupplier(Integer storedBlockHeight) { public SparrowChainSupplier(Integer storedBlockHeight) {
this.storedBlockHeight = AppServices.getCurrentBlockHeight() == null ? (storedBlockHeight != null ? storedBlockHeight : 0) : AppServices.getCurrentBlockHeight(); this.storedBlockHeight = AppServices.getCurrentBlockHeight() == null ? (storedBlockHeight != null ? storedBlockHeight : 0) : AppServices.getCurrentBlockHeight();
this.latestBlock = computeLatestBlock(); this.latestBlock = computeLatestBlock();
}
public void open() {
EventManager.get().register(this); EventManager.get().register(this);
} }

View file

@ -83,6 +83,7 @@ public class SparrowDataSource extends AbstractDataSource {
public void open(CoordinatorSupplier coordinatorSupplier) throws Exception { public void open(CoordinatorSupplier coordinatorSupplier) throws Exception {
super.open(coordinatorSupplier); super.open(coordinatorSupplier);
EventManager.get().register(this); EventManager.get().register(this);
((SparrowChainSupplier)getDataSourceConfig().getChainSupplier()).open();
} }
@Override @Override

View file

@ -128,7 +128,8 @@ public class SparrowUtxoSupplier extends BasicUtxoSupplier {
@Override @Override
public byte[] _getPrivKeyBip47(UnspentOutput utxo) throws Exception { 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<BlockTransactionHashIndex, WalletNode> walletUtxos = wallet.getWalletUtxos(); Map<BlockTransactionHashIndex, WalletNode> walletUtxos = wallet.getWalletUtxos();
WalletNode node = walletUtxos.entrySet().stream() WalletNode node = walletUtxos.entrySet().stream()
.filter(entry -> entry.getKey().getHash().equals(Sha256Hash.wrap(utxo.tx_hash)) && entry.getKey().getIndex() == utxo.tx_output_n) .filter(entry -> entry.getKey().getHash().equals(Sha256Hash.wrap(utxo.tx_hash)) && entry.getKey().getIndex() == utxo.tx_output_n)