mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-24 12:46:45 +00:00
support bitcoin core connections over tor
This commit is contained in:
parent
576253e651
commit
6f95dbe309
12 changed files with 62 additions and 61 deletions
|
@ -70,7 +70,6 @@ public class MainApp extends Application {
|
||||||
if(optionalMode.isPresent()) {
|
if(optionalMode.isPresent()) {
|
||||||
mode = optionalMode.get();
|
mode = optionalMode.get();
|
||||||
Config.get().setMode(mode);
|
Config.get().setMode(mode);
|
||||||
Config.get().setCoreWallet(Bwt.DEFAULT_CORE_WALLET);
|
|
||||||
|
|
||||||
if(mode.equals(Mode.ONLINE)) {
|
if(mode.equals(Mode.ONLINE)) {
|
||||||
PreferencesDialog preferencesDialog = new PreferencesDialog(PreferenceGroup.SERVER, true);
|
PreferencesDialog preferencesDialog = new PreferencesDialog(PreferenceGroup.SERVER, true);
|
||||||
|
@ -85,9 +84,6 @@ public class MainApp extends Application {
|
||||||
|
|
||||||
if(Config.get().getServerType() == null && Config.get().getCoreServer() == null && Config.get().getElectrumServer() != null) {
|
if(Config.get().getServerType() == null && Config.get().getCoreServer() == null && Config.get().getElectrumServer() != null) {
|
||||||
Config.get().setServerType(ServerType.ELECTRUM_SERVER);
|
Config.get().setServerType(ServerType.ELECTRUM_SERVER);
|
||||||
} else if(Config.get().getServerType() == ServerType.BITCOIN_CORE && Config.get().getCoreWallet() == null) {
|
|
||||||
Config.get().setCoreMultiWallet(Boolean.TRUE);
|
|
||||||
Config.get().setCoreWallet("");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Config.get().getHdCapture() == null && Platform.getCurrent() == Platform.OSX) {
|
if(Config.get().getHdCapture() == null && Platform.getCurrent() == Platform.OSX) {
|
||||||
|
|
|
@ -425,24 +425,6 @@ public class Config {
|
||||||
flush();
|
flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean getCoreMultiWallet() {
|
|
||||||
return coreMultiWallet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCoreMultiWallet(Boolean coreMultiWallet) {
|
|
||||||
this.coreMultiWallet = coreMultiWallet;
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCoreWallet() {
|
|
||||||
return coreWallet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCoreWallet(String coreWallet) {
|
|
||||||
this.coreWallet = coreWallet;
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getElectrumServer() {
|
public String getElectrumServer() {
|
||||||
return electrumServer;
|
return electrumServer;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package com.sparrowwallet.sparrow.net;
|
package com.sparrowwallet.sparrow.net;
|
||||||
|
|
||||||
|
import com.google.common.net.HostAndPort;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
import com.sparrowwallet.drongo.KeyPurpose;
|
import com.sparrowwallet.drongo.KeyPurpose;
|
||||||
|
@ -7,6 +8,7 @@ import com.sparrowwallet.drongo.Network;
|
||||||
import com.sparrowwallet.drongo.OutputDescriptor;
|
import com.sparrowwallet.drongo.OutputDescriptor;
|
||||||
import com.sparrowwallet.drongo.wallet.BlockTransactionHash;
|
import com.sparrowwallet.drongo.wallet.BlockTransactionHash;
|
||||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
|
import com.sparrowwallet.sparrow.AppServices;
|
||||||
import com.sparrowwallet.sparrow.EventManager;
|
import com.sparrowwallet.sparrow.EventManager;
|
||||||
import com.sparrowwallet.sparrow.event.*;
|
import com.sparrowwallet.sparrow.event.*;
|
||||||
import com.sparrowwallet.sparrow.io.Config;
|
import com.sparrowwallet.sparrow.io.Config;
|
||||||
|
@ -100,6 +102,7 @@ public class Bwt {
|
||||||
bwtConfig.gapLimit = gapLimit;
|
bwtConfig.gapLimit = gapLimit;
|
||||||
} else {
|
} else {
|
||||||
bwtConfig.requireAddresses = false;
|
bwtConfig.requireAddresses = false;
|
||||||
|
bwtConfig.bitcoindTimeout = 30;
|
||||||
}
|
}
|
||||||
|
|
||||||
bwtConfig.verbose = log.isDebugEnabled() ? 2 : 0;
|
bwtConfig.verbose = log.isDebugEnabled() ? 2 : 0;
|
||||||
|
@ -112,14 +115,19 @@ public class Bwt {
|
||||||
|
|
||||||
Config config = Config.get();
|
Config config = Config.get();
|
||||||
bwtConfig.bitcoindUrl = config.getCoreServer();
|
bwtConfig.bitcoindUrl = config.getCoreServer();
|
||||||
|
|
||||||
|
HostAndPort torProxy = getTorProxy();
|
||||||
|
if(Protocol.isOnionAddress(bwtConfig.bitcoindUrl) && torProxy != null) {
|
||||||
|
bwtConfig.bitcoindProxy = torProxy.toString();
|
||||||
|
}
|
||||||
|
|
||||||
if((config.getCoreAuthType() == CoreAuthType.COOKIE || config.getCoreAuth() == null || config.getCoreAuth().length() < 2) && config.getCoreDataDir() != null) {
|
if((config.getCoreAuthType() == CoreAuthType.COOKIE || config.getCoreAuth() == null || config.getCoreAuth().length() < 2) && config.getCoreDataDir() != null) {
|
||||||
bwtConfig.bitcoindDir = config.getCoreDataDir().getAbsolutePath() + "/";
|
bwtConfig.bitcoindDir = config.getCoreDataDir().getAbsolutePath() + "/";
|
||||||
} else {
|
} else {
|
||||||
bwtConfig.bitcoindAuth = config.getCoreAuth();
|
bwtConfig.bitcoindAuth = config.getCoreAuth();
|
||||||
}
|
}
|
||||||
if(config.getCoreMultiWallet() != Boolean.FALSE) {
|
|
||||||
bwtConfig.bitcoindWallet = config.getCoreWallet();
|
bwtConfig.bitcoindWallet = DEFAULT_CORE_WALLET;
|
||||||
}
|
|
||||||
bwtConfig.createWalletIfMissing = true;
|
bwtConfig.createWalletIfMissing = true;
|
||||||
|
|
||||||
Gson gson = new Gson();
|
Gson gson = new Gson();
|
||||||
|
@ -129,6 +137,12 @@ public class Bwt {
|
||||||
NativeBwtDaemon.start(jsonConfig, callback);
|
NativeBwtDaemon.start(jsonConfig, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private HostAndPort getTorProxy() {
|
||||||
|
return AppServices.isTorRunning() ?
|
||||||
|
HostAndPort.fromParts("127.0.0.1", TorService.PROXY_PORT) :
|
||||||
|
(Config.get().getProxyServer() == null || Config.get().getProxyServer().isEmpty() || !Config.get().isUseProxy() ? null : HostAndPort.fromString(Config.get().getProxyServer().replace("localhost", "127.0.0.1")));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shut down the BWT daemon
|
* Shut down the BWT daemon
|
||||||
*
|
*
|
||||||
|
@ -184,6 +198,12 @@ public class Bwt {
|
||||||
@SerializedName("bitcoind_wallet")
|
@SerializedName("bitcoind_wallet")
|
||||||
public String bitcoindWallet;
|
public String bitcoindWallet;
|
||||||
|
|
||||||
|
@SerializedName("bitcoind_proxy")
|
||||||
|
public String bitcoindProxy;
|
||||||
|
|
||||||
|
@SerializedName("bitcoind_timeout")
|
||||||
|
public Integer bitcoindTimeout;
|
||||||
|
|
||||||
@SerializedName("create_wallet_if_missing")
|
@SerializedName("create_wallet_if_missing")
|
||||||
public Boolean createWalletIfMissing;
|
public Boolean createWalletIfMissing;
|
||||||
|
|
||||||
|
|
|
@ -102,6 +102,6 @@ public final class IpAddressMatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isLocalNetworkAddress(String address) {
|
public static boolean isLocalNetworkAddress(String address) {
|
||||||
return LOCAL_RANGE_1.matches(address) || LOCAL_RANGE_2.matches(address) || LOCAL_RANGE_3.matches(address);
|
return "localhost".equals(address) || "127.0.0.1".equals(address) || LOCAL_RANGE_1.matches(address) || LOCAL_RANGE_2.matches(address) || LOCAL_RANGE_3.matches(address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,10 +116,21 @@ public enum Protocol {
|
||||||
return toUrlString() + hostAndPort.toString();
|
return toUrlString() + hostAndPort.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isOnionAddress(HostAndPort server) {
|
public static boolean isOnionAddress(HostAndPort server) {
|
||||||
return server.getHost().toLowerCase().endsWith(TorService.TOR_ADDRESS_SUFFIX);
|
return server.getHost().toLowerCase().endsWith(TorService.TOR_ADDRESS_SUFFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isOnionAddress(String address) {
|
||||||
|
if(address != null) {
|
||||||
|
Protocol protocol = Protocol.getProtocol(address);
|
||||||
|
if(protocol != null) {
|
||||||
|
return isOnionAddress(protocol.getServerHostAndPort(address));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static Protocol getProtocol(String url) {
|
public static Protocol getProtocol(String url) {
|
||||||
if(url.startsWith("tcp://")) {
|
if(url.startsWith("tcp://")) {
|
||||||
return TCP;
|
return TCP;
|
||||||
|
|
|
@ -26,11 +26,13 @@ public class TcpTransport implements Transport, Closeable {
|
||||||
|
|
||||||
public static final int DEFAULT_PORT = 50001;
|
public static final int DEFAULT_PORT = 50001;
|
||||||
private static final int[] BASE_READ_TIMEOUT_SECS = {3, 8, 16, 34};
|
private static final int[] BASE_READ_TIMEOUT_SECS = {3, 8, 16, 34};
|
||||||
|
private static final int[] SLOW_READ_TIMEOUT_SECS = {34, 68, 124, 208};
|
||||||
public static final long PER_REQUEST_READ_TIMEOUT_MILLIS = 50;
|
public static final long PER_REQUEST_READ_TIMEOUT_MILLIS = 50;
|
||||||
public static final int SOCKET_READ_TIMEOUT_MILLIS = 5000;
|
public static final int SOCKET_READ_TIMEOUT_MILLIS = 5000;
|
||||||
|
|
||||||
protected final HostAndPort server;
|
protected final HostAndPort server;
|
||||||
protected final SocketFactory socketFactory;
|
protected final SocketFactory socketFactory;
|
||||||
|
protected final int[] readTimeouts;
|
||||||
|
|
||||||
private Socket socket;
|
private Socket socket;
|
||||||
|
|
||||||
|
@ -59,6 +61,7 @@ public class TcpTransport implements Transport, Closeable {
|
||||||
public TcpTransport(HostAndPort server, HostAndPort proxy) {
|
public TcpTransport(HostAndPort server, HostAndPort proxy) {
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.socketFactory = (proxy == null ? SocketFactory.getDefault() : new ProxySocketFactory(proxy));
|
this.socketFactory = (proxy == null ? SocketFactory.getDefault() : new ProxySocketFactory(proxy));
|
||||||
|
this.readTimeouts = (Config.get().getServerType() == ServerType.BITCOIN_CORE && Protocol.isOnionAddress(Config.get().getCoreServer()) ? SLOW_READ_TIMEOUT_SECS : BASE_READ_TIMEOUT_SECS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -91,16 +94,16 @@ public class TcpTransport implements Transport, Closeable {
|
||||||
|
|
||||||
private String readResponse() throws IOException {
|
private String readResponse() throws IOException {
|
||||||
try {
|
try {
|
||||||
if(!readLock.tryLock((BASE_READ_TIMEOUT_SECS[readTimeoutIndex] * 1000) + (requestIdCount * PER_REQUEST_READ_TIMEOUT_MILLIS), TimeUnit.MILLISECONDS)) {
|
if(!readLock.tryLock((readTimeouts[readTimeoutIndex] * 1000L) + (requestIdCount * PER_REQUEST_READ_TIMEOUT_MILLIS), TimeUnit.MILLISECONDS)) {
|
||||||
readTimeoutIndex = Math.min(readTimeoutIndex + 1, BASE_READ_TIMEOUT_SECS.length - 1);
|
readTimeoutIndex = Math.min(readTimeoutIndex + 1, readTimeouts.length - 1);
|
||||||
log.warn("No response from server, setting read timeout to " + BASE_READ_TIMEOUT_SECS[readTimeoutIndex] + " secs");
|
log.warn("No response from server, setting read timeout to " + readTimeouts[readTimeoutIndex] + " secs");
|
||||||
throw new IOException("No response from server");
|
throw new IOException("No response from server");
|
||||||
}
|
}
|
||||||
} catch(InterruptedException e) {
|
} catch(InterruptedException e) {
|
||||||
throw new IOException("Read thread interrupted");
|
throw new IOException("Read thread interrupted");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(readTimeoutIndex == BASE_READ_TIMEOUT_SECS.length - 1) {
|
if(readTimeoutIndex == readTimeouts.length - 1) {
|
||||||
readTimeoutIndex--;
|
readTimeoutIndex--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -102,10 +102,13 @@ public class ServerPreferencesController extends PreferencesDetailController {
|
||||||
private PasswordField corePass;
|
private PasswordField corePass;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private UnlabeledToggleSwitch coreMultiWallet;
|
private UnlabeledToggleSwitch coreUseProxy;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private TextField coreWallet;
|
private TextField coreProxyHost;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TextField coreProxyPort;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Form electrumForm;
|
private Form electrumForm;
|
||||||
|
@ -209,7 +212,9 @@ public class ServerPreferencesController extends PreferencesDetailController {
|
||||||
coreUser.textProperty().addListener(getBitcoinAuthListener(config));
|
coreUser.textProperty().addListener(getBitcoinAuthListener(config));
|
||||||
corePass.textProperty().addListener(getBitcoinAuthListener(config));
|
corePass.textProperty().addListener(getBitcoinAuthListener(config));
|
||||||
|
|
||||||
coreWallet.textProperty().addListener(getBitcoinWalletListener(config));
|
coreUseProxy.selectedProperty().bindBidirectional(useProxy.selectedProperty());
|
||||||
|
coreProxyHost.textProperty().bindBidirectional(proxyHost.textProperty());
|
||||||
|
coreProxyPort.textProperty().bindBidirectional(proxyPort.textProperty());
|
||||||
|
|
||||||
electrumHost.textProperty().addListener(getElectrumServerListener(config));
|
electrumHost.textProperty().addListener(getElectrumServerListener(config));
|
||||||
electrumPort.textProperty().addListener(getElectrumServerListener(config));
|
electrumPort.textProperty().addListener(getElectrumServerListener(config));
|
||||||
|
@ -250,11 +255,6 @@ public class ServerPreferencesController extends PreferencesDetailController {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
coreMultiWallet.selectedProperty().addListener((observable, oldValue, newValue) -> {
|
|
||||||
config.setCoreMultiWallet(newValue);
|
|
||||||
coreWallet.setDisable(!newValue);
|
|
||||||
});
|
|
||||||
|
|
||||||
electrumUseSsl.selectedProperty().addListener((observable, oldValue, newValue) -> {
|
electrumUseSsl.selectedProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
setElectrumServerInConfig(config);
|
setElectrumServerInConfig(config);
|
||||||
electrumCertificate.setDisable(!newValue);
|
electrumCertificate.setDisable(!newValue);
|
||||||
|
@ -358,15 +358,6 @@ public class ServerPreferencesController extends PreferencesDetailController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
coreWallet.setPromptText("Default");
|
|
||||||
if(config.getCoreWallet() == null) {
|
|
||||||
coreWallet.setText(Bwt.DEFAULT_CORE_WALLET);
|
|
||||||
} else {
|
|
||||||
coreWallet.setText(config.getCoreWallet());
|
|
||||||
}
|
|
||||||
|
|
||||||
coreMultiWallet.setSelected(config.getCoreMultiWallet() != Boolean.FALSE);
|
|
||||||
|
|
||||||
String electrumServer = config.getElectrumServer();
|
String electrumServer = config.getElectrumServer();
|
||||||
if(electrumServer != null) {
|
if(electrumServer != null) {
|
||||||
Protocol protocol = Protocol.getProtocol(electrumServer);
|
Protocol protocol = Protocol.getProtocol(electrumServer);
|
||||||
|
@ -519,8 +510,9 @@ public class ServerPreferencesController extends PreferencesDetailController {
|
||||||
coreDataDirSelect.setDisable(!editable);
|
coreDataDirSelect.setDisable(!editable);
|
||||||
coreUser.setDisable(!editable);
|
coreUser.setDisable(!editable);
|
||||||
corePass.setDisable(!editable);
|
corePass.setDisable(!editable);
|
||||||
coreMultiWallet.setDisable(!editable);
|
coreUseProxy.setDisable(!editable);
|
||||||
coreWallet.setDisable(!editable);
|
coreProxyHost.setDisable(!editable);
|
||||||
|
coreProxyPort.setDisable(!editable);
|
||||||
|
|
||||||
electrumHost.setDisable(!editable);
|
electrumHost.setDisable(!editable);
|
||||||
electrumPort.setDisable(!editable);
|
electrumPort.setDisable(!editable);
|
||||||
|
@ -665,13 +657,6 @@ public class ServerPreferencesController extends PreferencesDetailController {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private ChangeListener<String> getBitcoinWalletListener(Config config) {
|
|
||||||
return (observable, oldValue, newValue) -> {
|
|
||||||
config.setCoreWallet(coreWallet.getText());
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private ChangeListener<String> getElectrumServerListener(Config config) {
|
private ChangeListener<String> getElectrumServerListener(Config config) {
|
||||||
return (observable, oldValue, newValue) -> {
|
return (observable, oldValue, newValue) -> {
|
||||||
|
|
|
@ -49,3 +49,6 @@
|
||||||
-fx-text-fill: linear-gradient(to bottom, derive(#0b99c9, 30%), #0b99c9);
|
-fx-text-fill: linear-gradient(to bottom, derive(#0b99c9, 30%), #0b99c9);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#electrumUseSsl {
|
||||||
|
-fx-padding: 4 0 2 0;
|
||||||
|
}
|
|
@ -144,11 +144,12 @@
|
||||||
<TextField fx:id="coreUser"/>
|
<TextField fx:id="coreUser"/>
|
||||||
<PasswordField fx:id="corePass"/>
|
<PasswordField fx:id="corePass"/>
|
||||||
</Field>
|
</Field>
|
||||||
<Field text="Multi-Wallet:">
|
<Field text="Use Proxy:">
|
||||||
<UnlabeledToggleSwitch fx:id="coreMultiWallet"/> <HelpLabel helpText="Creates a new Bitcoin Core wallet with the following name (recommended to avoid conflicts)" />
|
<UnlabeledToggleSwitch fx:id="coreUseProxy"/>
|
||||||
</Field>
|
</Field>
|
||||||
<Field text="Wallet Name:" styleClass="label-button">
|
<Field text="Proxy URL:">
|
||||||
<TextField fx:id="coreWallet"/>
|
<TextField fx:id="coreProxyHost" />
|
||||||
|
<TextField fx:id="coreProxyPort" maxWidth="100" />
|
||||||
</Field>
|
</Field>
|
||||||
</Fieldset>
|
</Fieldset>
|
||||||
</Form>
|
</Form>
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in a new issue