add public electrum server type

This commit is contained in:
Craig Raw 2021-02-16 16:38:36 +02:00
parent 48a63c25d2
commit 454e4b4c4b
9 changed files with 221 additions and 11 deletions

View file

@ -542,7 +542,18 @@ public class AppController implements Initializable {
}
private void setServerToggleTooltip(Integer currentBlockHeight) {
serverToggle.setTooltip(new Tooltip(AppServices.isConnected() ? "Connected to " + Config.get().getServerAddress() + (currentBlockHeight != null ? " at height " + currentBlockHeight : "") : "Disconnected"));
Tooltip tooltip = new Tooltip(getServerToggleTooltipText(currentBlockHeight));
tooltip.setShowDuration(Duration.seconds(15));
serverToggle.setTooltip(tooltip);
}
private String getServerToggleTooltipText(Integer currentBlockHeight) {
if(AppServices.isConnected()) {
return "Connected to " + Config.get().getServerAddress() + (currentBlockHeight != null ? " at height " + currentBlockHeight : "") +
(Config.get().getServerType() == ServerType.PUBLIC_ELECTRUM_SERVER ? "\nWarning! You are connected to a public server and sharing your transaction data with it.\nFor better privacy, consider using your own Bitcoin Core node or private Electrum server." : "");
}
return "Disconnected";
}
public void newWallet(ActionEvent event) {
@ -1069,6 +1080,12 @@ public class AppController implements Initializable {
}
public void setServerType(ServerType serverType) {
if(serverType == ServerType.PUBLIC_ELECTRUM_SERVER && !serverToggle.getStyleClass().contains("public-server")) {
serverToggle.getStyleClass().add("public-server");
} else {
serverToggle.getStyleClass().remove("public-server");
}
if(serverType == ServerType.BITCOIN_CORE && !serverToggle.getStyleClass().contains("core-server")) {
serverToggle.getStyleClass().add("core-server");
} else {

View file

@ -38,6 +38,7 @@ public class Config {
private File hwi;
private boolean hdCapture;
private ServerType serverType;
private String publicElectrumServer;
private String coreServer;
private CoreAuthType coreAuthType;
private File coreDataDir;
@ -275,7 +276,7 @@ public class Config {
}
public String getServerAddress() {
return getServerType() == ServerType.BITCOIN_CORE ? getCoreServer() : getElectrumServer();
return getServerType() == ServerType.BITCOIN_CORE ? getCoreServer() : (getServerType() == ServerType.PUBLIC_ELECTRUM_SERVER ? getPublicElectrumServer() : getElectrumServer());
}
public boolean requiresTor() {
@ -291,6 +292,15 @@ public class Config {
return protocol.isOnionAddress(protocol.getServerHostAndPort(getServerAddress()));
}
public String getPublicElectrumServer() {
return publicElectrumServer;
}
public void setPublicElectrumServer(String publicElectrumServer) {
this.publicElectrumServer = publicElectrumServer;
flush();
}
public String getCoreServer() {
return coreServer;
}

View file

@ -53,7 +53,10 @@ public class ElectrumServer {
File electrumServerCert = null;
String proxyServer = null;
if(Config.get().getServerType() == ServerType.BITCOIN_CORE) {
if(Config.get().getServerType() == ServerType.PUBLIC_ELECTRUM_SERVER) {
electrumServer = Config.get().getPublicElectrumServer();
proxyServer = Config.get().getProxyServer();
} else if(Config.get().getServerType() == ServerType.BITCOIN_CORE) {
if(bwtElectrumServer == null) {
throw new ServerConfigException("Could not connect to Bitcoin Core RPC");
}

View file

@ -0,0 +1,38 @@
package com.sparrowwallet.sparrow.net;
public enum PublicElectrumServer {
BLOCKSTREAM_INFO("blockstream.info", "ssl://blockstream.info:700"),
ELECTRUM_BLOCKSTREAM_INFO("electrum.blockstream.info", "ssl://electrum.blockstream.info:50002"),
LUKECHILDS_CO("bitcoin.lukechilds.co", "ssl://bitcoin.lukechilds.co:50002");
PublicElectrumServer(String name, String url) {
this.name = name;
this.url = url;
}
private final String name;
private final String url;
public String getName() {
return name;
}
public String getUrl() {
return url;
}
public static PublicElectrumServer fromUrl(String url) {
for(PublicElectrumServer server : values()) {
if(server.url.equals(url)) {
return server;
}
}
return null;
}
@Override
public String toString() {
return name;
}
}

View file

@ -1,7 +1,7 @@
package com.sparrowwallet.sparrow.net;
public enum ServerType {
BITCOIN_CORE("Bitcoin Core"), ELECTRUM_SERVER("Electrum Server");
BITCOIN_CORE("Bitcoin Core"), ELECTRUM_SERVER("Private Electrum"), PUBLIC_ELECTRUM_SERVER("Public Electrum");
private final String name;

View file

@ -46,7 +46,7 @@ public class PreferencesDialog extends Dialog<Boolean> {
dialogPane.getButtonTypes().addAll(newWalletButtonType);
}
dialogPane.setPrefWidth(650);
dialogPane.setPrefWidth(710);
dialogPane.setPrefHeight(600);
preferencesController.reconnectOnClosingProperty().set(AppServices.isConnecting() || AppServices.isConnected());

View file

@ -23,6 +23,7 @@ import javafx.stage.Stage;
import javafx.util.Duration;
import net.freehaven.tor.control.TorControlError;
import org.berndpruenster.netlayer.tor.Tor;
import org.controlsfx.control.SegmentedButton;
import org.controlsfx.glyphfont.Glyph;
import org.controlsfx.validation.ValidationResult;
import org.controlsfx.validation.ValidationSupport;
@ -41,6 +42,7 @@ import java.security.cert.CertificateFactory;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Random;
public class ServerPreferencesController extends PreferencesDetailController {
private static final Logger log = LoggerFactory.getLogger(ServerPreferencesController.class);
@ -48,6 +50,27 @@ public class ServerPreferencesController extends PreferencesDetailController {
@FXML
private ToggleGroup serverTypeToggleGroup;
@FXML
private SegmentedButton serverTypeSegmentedButton;
@FXML
private ToggleButton publicElectrumToggle;
@FXML
private Form publicElectrumForm;
@FXML
private ComboBox<PublicElectrumServer> publicElectrumServer;
@FXML
private UnlabeledToggleSwitch publicUseProxy;
@FXML
private TextField publicProxyHost;
@FXML
private TextField publicProxyPort;
@FXML
private Form coreForm;
@ -135,13 +158,15 @@ public class ServerPreferencesController extends PreferencesDetailController {
Platform.runLater(this::setupValidation);
publicElectrumForm.managedProperty().bind(publicElectrumForm.visibleProperty());
coreForm.managedProperty().bind(coreForm.visibleProperty());
electrumForm.managedProperty().bind(electrumForm.visibleProperty());
coreForm.visibleProperty().bind(electrumForm.visibleProperty().not());
serverTypeToggleGroup.selectedToggleProperty().addListener((observable, oldValue, newValue) -> {
if(serverTypeToggleGroup.getSelectedToggle() != null) {
ServerType existingType = config.getServerType();
ServerType serverType = (ServerType)newValue.getUserData();
publicElectrumForm.setVisible(serverType == ServerType.PUBLIC_ELECTRUM_SERVER);
coreForm.setVisible(serverType == ServerType.BITCOIN_CORE);
electrumForm.setVisible(serverType == ServerType.ELECTRUM_SERVER);
config.setServerType(serverType);
testConnection.setGraphic(getGlyph(FontAwesome5.Glyph.QUESTION_CIRCLE, ""));
@ -153,12 +178,26 @@ public class ServerPreferencesController extends PreferencesDetailController {
oldValue.setSelected(true);
}
});
ServerType serverType = config.getServerType() != null ? config.getServerType() : (config.getCoreServer() == null && config.getElectrumServer() != null ? ServerType.ELECTRUM_SERVER : ServerType.BITCOIN_CORE);
ServerType serverType = config.getServerType() != null ?
(config.getServerType() == ServerType.PUBLIC_ELECTRUM_SERVER && Network.get() != Network.MAINNET ? ServerType.BITCOIN_CORE : config.getServerType()) :
(config.getCoreServer() == null && config.getElectrumServer() != null ? ServerType.ELECTRUM_SERVER :
(config.getCoreServer() != null || Network.get() != Network.MAINNET ? ServerType.BITCOIN_CORE : ServerType.PUBLIC_ELECTRUM_SERVER));
if(Network.get() != Network.MAINNET) {
serverTypeSegmentedButton.getButtons().remove(publicElectrumToggle);
serverTypeToggleGroup.getToggles().remove(publicElectrumToggle);
}
serverTypeToggleGroup.selectToggle(serverTypeToggleGroup.getToggles().stream().filter(toggle -> toggle.getUserData() == serverType).findFirst().orElse(null));
publicElectrumServer.getSelectionModel().selectedItemProperty().addListener(getPublicElectrumServerListener(config));
publicUseProxy.selectedProperty().bindBidirectional(useProxy.selectedProperty());
publicProxyHost.textProperty().bindBidirectional(proxyHost.textProperty());
publicProxyPort.textProperty().bindBidirectional(proxyPort.textProperty());
corePort.setTextFormatter(new TextFieldValidator(TextFieldValidator.ValidationModus.MAX_INTEGERS, 5).getFormatter());
electrumPort.setTextFormatter(new TextFieldValidator(TextFieldValidator.ValidationModus.MAX_INTEGERS, 5).getFormatter());
proxyPort.setTextFormatter(new TextFieldValidator(TextFieldValidator.ValidationModus.MAX_INTEGERS, 5).getFormatter());
publicProxyPort.setTextFormatter(new TextFieldValidator(TextFieldValidator.ValidationModus.MAX_INTEGERS, 5).getFormatter());
coreHost.textProperty().addListener(getBitcoinCoreListener(config));
corePort.textProperty().addListener(getBitcoinCoreListener(config));
@ -245,6 +284,8 @@ public class ServerPreferencesController extends PreferencesDetailController {
proxyHost.setText(proxyHost.getText().trim());
proxyHost.setDisable(!newValue);
proxyPort.setDisable(!newValue);
publicProxyHost.setDisable(!newValue);
publicProxyPort.setDisable(!newValue);
});
boolean isConnected = AppServices.isConnecting() || AppServices.isConnected();
@ -277,6 +318,13 @@ public class ServerPreferencesController extends PreferencesDetailController {
testConnection.setVisible(true);
});
PublicElectrumServer configPublicElectrumServer = PublicElectrumServer.fromUrl(config.getPublicElectrumServer());
if(configPublicElectrumServer == null) {
publicElectrumServer.setValue(PublicElectrumServer.values()[new Random().nextInt(PublicElectrumServer.values().length)]);
} else {
publicElectrumServer.setValue(configPublicElectrumServer);
}
String coreServer = config.getCoreServer();
if(coreServer != null) {
Protocol protocol = Protocol.getProtocol(coreServer);
@ -340,6 +388,8 @@ public class ServerPreferencesController extends PreferencesDetailController {
useProxy.setSelected(config.isUseProxy());
proxyHost.setDisable(!config.isUseProxy());
proxyPort.setDisable(!config.isUseProxy());
publicProxyHost.setDisable(!config.isUseProxy());
publicProxyPort.setDisable(!config.isUseProxy());
String proxyServer = config.getProxyServer();
if(proxyServer != null) {
@ -407,6 +457,11 @@ public class ServerPreferencesController extends PreferencesDetailController {
private void setFieldsEditable(boolean editable) {
serverTypeToggleGroup.getToggles().forEach(toggle -> ((ToggleButton)toggle).setDisable(!editable));
publicElectrumServer.setDisable(!editable);
publicUseProxy.setDisable(!editable);
publicProxyHost.setDisable(!editable);
publicProxyPort.setDisable(!editable);
coreHost.setDisable(!editable);
corePort.setDisable(!editable);
coreAuthToggleGroup.getToggles().forEach(toggle -> ((ToggleButton)toggle).setDisable(!editable));
@ -455,6 +510,15 @@ public class ServerPreferencesController extends PreferencesDetailController {
}
private void setupValidation() {
validationSupport.registerValidator(publicProxyHost, Validator.combine(
(Control c, String newValue) -> ValidationResult.fromErrorIf( c, "Proxy host required", publicUseProxy.isSelected() && newValue.isEmpty()),
(Control c, String newValue) -> ValidationResult.fromErrorIf( c, "Invalid host name", getHost(newValue) == null)
));
validationSupport.registerValidator(publicProxyPort, Validator.combine(
(Control c, String newValue) -> ValidationResult.fromErrorIf( c, "Invalid proxy port", !newValue.isEmpty() && !isValidPort(Integer.parseInt(newValue)))
));
validationSupport.registerValidator(coreHost, Validator.combine(
(Control c, String newValue) -> ValidationResult.fromErrorIf( c, "Invalid Core host", getHost(newValue) == null)
));
@ -499,6 +563,13 @@ public class ServerPreferencesController extends PreferencesDetailController {
validationSupport.setValidationDecorator(new StyleClassValidationDecoration());
}
@NotNull
private ChangeListener<PublicElectrumServer> getPublicElectrumServerListener(Config config) {
return (observable, oldValue, newValue) -> {
config.setPublicElectrumServer(newValue.getUrl());
};
}
@NotNull
private ChangeListener<String> getBitcoinCoreListener(Config config) {
return (observable, oldValue, newValue) -> {

View file

@ -31,4 +31,21 @@
.scroll-pane {
-fx-background-color: transparent;
}
}
.public-warning {
-fx-text-fill: rgb(238, 210, 2);
}
.public-electrum {
-fx-text-fill: linear-gradient(to bottom, derive(rgb(238, 210, 2), 30%), rgb(238, 210, 2));
}
.bitcoin-core {
-fx-text-fill: linear-gradient(to bottom, derive(#50a14f, 30%), #50a14f);
}
.private-electrum {
-fx-text-fill: linear-gradient(to bottom, derive(#0b99c9, 30%), #0b99c9);
}

View file

@ -16,6 +16,9 @@
<?import com.sparrowwallet.sparrow.net.ServerType?>
<?import com.sparrowwallet.sparrow.net.CoreAuthType?>
<?import com.sparrowwallet.sparrow.control.HelpLabel?>
<?import javafx.collections.FXCollections?>
<?import com.sparrowwallet.sparrow.net.PublicElectrumServer?>
<?import com.sparrowwallet.sparrow.control.CopyableLabel?>
<GridPane hgap="10.0" vgap="10.0" stylesheets="@preferences.css, @../general.css" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.sparrowwallet.sparrow.preferences.ServerPreferencesController">
<padding>
<Insets left="25.0" right="25.0" top="25.0" />
@ -30,17 +33,31 @@
<Form GridPane.columnIndex="0" GridPane.rowIndex="0">
<Fieldset inputGrow="SOMETIMES" text="Server">
<Field text="Type:">
<SegmentedButton>
<SegmentedButton fx:id="serverTypeSegmentedButton">
<toggleGroup>
<ToggleGroup fx:id="serverTypeToggleGroup" />
</toggleGroup>
<buttons>
<ToggleButton fx:id="publicElectrumToggle" text="Public Electrum" toggleGroup="$serverTypeToggleGroup">
<graphic>
<Glyph fontFamily="Font Awesome 5 Free Solid" fontSize="12" icon="TOGGLE_ON" styleClass="public-electrum" />
</graphic>
<userData>
<ServerType fx:constant="PUBLIC_ELECTRUM_SERVER"/>
</userData>
</ToggleButton>
<ToggleButton text="Bitcoin Core" toggleGroup="$serverTypeToggleGroup">
<graphic>
<Glyph fontFamily="Font Awesome 5 Free Solid" fontSize="12" icon="TOGGLE_ON" styleClass="bitcoin-core" />
</graphic>
<userData>
<ServerType fx:constant="BITCOIN_CORE"/>
</userData>
</ToggleButton>
<ToggleButton text="Electrum Server" toggleGroup="$serverTypeToggleGroup">
<ToggleButton text="Private Electrum" toggleGroup="$serverTypeToggleGroup">
<graphic>
<Glyph fontFamily="Font Awesome 5 Free Solid" fontSize="12" icon="TOGGLE_ON" styleClass="private-electrum" />
</graphic>
<userData>
<ServerType fx:constant="ELECTRUM_SERVER"/>
</userData>
@ -51,6 +68,43 @@
</Fieldset>
</Form>
<Form fx:id="publicElectrumForm" GridPane.columnIndex="0" GridPane.rowIndex="1">
<Fieldset inputGrow="SOMETIMES" text="Public Electrum Server">
<Field text="URL:">
<ComboBox fx:id="publicElectrumServer">
<items>
<FXCollections fx:factory="observableArrayList">
<PublicElectrumServer fx:constant="BLOCKSTREAM_INFO" />
<PublicElectrumServer fx:constant="ELECTRUM_BLOCKSTREAM_INFO" />
<PublicElectrumServer fx:constant="LUKECHILDS_CO" />
</FXCollections>
</items>
</ComboBox>
</Field>
<Field text="">
<Label alignment="CENTER">
<graphic>
<Glyph fontFamily="Font Awesome 5 Free Solid" fontSize="12" icon="EXCLAMATION_TRIANGLE" styleClass="public-warning" />
</graphic>
<padding>
<Insets right="5" />
</padding>
</Label>
<CopyableLabel text="Warning!" />
</Field>
<Field text="">
<CopyableLabel text="Using a public server means it can see your transactions."/>
</Field>
<Field text="Use Proxy:">
<UnlabeledToggleSwitch fx:id="publicUseProxy"/>
</Field>
<Field text="Proxy URL:">
<TextField fx:id="publicProxyHost" />
<TextField fx:id="publicProxyPort" prefWidth="80" />
</Field>
</Fieldset>
</Form>
<Form fx:id="coreForm" GridPane.columnIndex="0" GridPane.rowIndex="1">
<Fieldset inputGrow="SOMETIMES" text="Bitcoin Core RPC">
<Field text="URL:">
@ -98,7 +152,7 @@
</Form>
<Form fx:id="electrumForm" GridPane.columnIndex="0" GridPane.rowIndex="1">
<Fieldset inputGrow="SOMETIMES" text="Electrum Server">
<Fieldset inputGrow="SOMETIMES" text="Private Electrum Server">
<Field text="URL:">
<TextField fx:id="electrumHost" promptText="e.g. 127.0.0.1"/>
<TextField fx:id="electrumPort" promptText="e.g. 50002" prefWidth="80" />