Add btc network selector to preferences

This commit is contained in:
Manfred Karrer 2015-05-10 13:31:09 +02:00
parent c1e0524090
commit 844a6dd66c
17 changed files with 248 additions and 82 deletions

View File

@ -22,7 +22,6 @@ import io.bitsquare.p2p.BootstrapNodes;
import io.bitsquare.p2p.Node;
import java.util.Collection;
import java.util.Random;
import java.util.stream.Collectors;
import net.tomp2p.dht.PeerBuilderDHT;
@ -45,6 +44,9 @@ public class BootstrapNode {
private static final String VERSION = "0.1.3";
public static final String P2P_ID = "node.p2pId";
public static int DEFAULT_P2P_ID = 1; // 0 | 1 | 2 for mainnet/testnet/regtest
private static Peer peer = null;
private final Environment env;
@ -56,12 +58,13 @@ public class BootstrapNode {
public void start() {
int port = env.getProperty(Node.PORT_KEY, Integer.class, BootstrapNodes.PORT);
String name = env.getProperty(Node.NAME_KEY, BootstrapNodes.DEFAULT_NODE_NAME);
int p2pId = env.getProperty(P2P_ID, Integer.class, DEFAULT_P2P_ID);
int port = env.getProperty(Node.PORT_KEY, Integer.class, BootstrapNodes.BASE_PORT + p2pId);
String name = env.getRequiredProperty(Node.NAME_KEY);
Logging.setup(name + "_" + port);
try {
Number160 peerId = Number160.createHash(new Random().nextInt());
Number160 peerId = Number160.createHash(name);
/*
DefaultEventExecutorGroup eventExecutorGroup = new DefaultEventExecutorGroup(50);
ChannelClientConfiguration clientConf = PeerBuilder.createDefaultChannelClientConfiguration();
@ -73,6 +76,7 @@ public class BootstrapNode {
peer = new PeerBuilder(peerId)
.ports(port)
.p2pId(p2pId)
/* .channelClientConfiguration(clientConf)
.channelServerConfiguration(serverConf)*/
.start();
@ -87,7 +91,7 @@ public class BootstrapNode {
if (!name.equals(BootstrapNodes.LOCALHOST.getName())) {
Collection<PeerAddress> bootstrapNodes = BootstrapNodes.getAllBootstrapNodes().stream().filter(e -> !e.getName().equals(name))
.map(e -> e.toPeerAddress()).collect(Collectors.toList());
.map(e -> e.toPeerAddressWithPort(port)).collect(Collectors.toList());
log.info("Bootstrapping to " + bootstrapNodes.size() + " bootstrapNode(s)");
log.info("Bootstrapping bootstrapNodes " + bootstrapNodes);

View File

@ -32,9 +32,13 @@ public class BootstrapNodeMain extends BitsquareExecutable {
}
protected void customizeOptionParsing(OptionParser parser) {
parser.accepts(Node.NAME_KEY, description("Name of this node", BootstrapNodes.DEFAULT_NODE_NAME))
.withRequiredArg();
parser.accepts(Node.PORT_KEY, description("Port to listen on", BootstrapNodes.PORT))
parser.accepts(Node.NAME_KEY, description("Name of this node", null))
.withRequiredArg()
.isRequired();
parser.accepts(Node.PORT_KEY, description("Port to listen on", BootstrapNodes.BASE_PORT))
.withRequiredArg()
.ofType(int.class);
parser.accepts(BootstrapNode.P2P_ID, description("P2P Network ID [0 | 1 | 2 for mainnet/testnet/regtest]", BootstrapNode.DEFAULT_P2P_ID))
.withRequiredArg()
.ofType(int.class);
}

View File

@ -18,7 +18,6 @@
package io.bitsquare.app;
import io.bitsquare.BitsquareException;
import io.bitsquare.btc.BitcoinNetwork;
import io.bitsquare.btc.UserAgent;
import io.bitsquare.btc.WalletService;
import io.bitsquare.crypto.KeyStorage;
@ -88,12 +87,8 @@ public class BitsquareEnvironment extends StandardEnvironment {
(String) commandLineProperties.getProperty(APP_DATA_DIR_KEY) :
appDataDir(userDataDir, appName);
String bitcoinNetwork = commandLineProperties.containsProperty(BitcoinNetwork.KEY) ?
(String) commandLineProperties.getProperty(BitcoinNetwork.KEY) :
BitcoinNetwork.DEFAULT.toString();
this.bootstrapNodePort = commandLineProperties.containsProperty(TomP2PModule.BOOTSTRAP_NODE_PORT_KEY) ?
(String) commandLineProperties.getProperty(TomP2PModule.BOOTSTRAP_NODE_PORT_KEY) : String.valueOf(BootstrapNodes.PORT);
(String) commandLineProperties.getProperty(TomP2PModule.BOOTSTRAP_NODE_PORT_KEY) : String.valueOf(BootstrapNodes.BASE_PORT);
MutablePropertySources propertySources = this.getPropertySources();
propertySources.addFirst(commandLineProperties);

View File

@ -40,7 +40,6 @@ public class BitcoinModule extends BitsquareModule {
@Override
protected void configure() {
bind(BitcoinNetwork.class).toInstance(env.getProperty(BitcoinNetwork.KEY, BitcoinNetwork.class, BitcoinNetwork.DEFAULT));
bind(RegTestHost.class).toInstance(env.getProperty(RegTestHost.KEY, RegTestHost.class, RegTestHost.DEFAULT));
bind(FeePolicy.class).in(Singleton.class);

View File

@ -22,7 +22,9 @@ import org.bitcoinj.params.MainNetParams;
import org.bitcoinj.params.RegTestParams;
import org.bitcoinj.params.TestNet3Params;
public enum BitcoinNetwork {
import java.io.Serializable;
public enum BitcoinNetwork implements Serializable {
MAINNET(MainNetParams.get()),
TESTNET(TestNet3Params.get()),

View File

@ -18,6 +18,7 @@
package io.bitsquare.btc;
import io.bitsquare.BitsquareException;
import io.bitsquare.user.Preferences;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.AddressFormatException;
@ -41,8 +42,8 @@ public class FeePolicy {
private final String takeOfferFeeAddress;
@Inject
public FeePolicy(BitcoinNetwork bitcoinNetwork) {
this.bitcoinNetwork = bitcoinNetwork;
public FeePolicy(Preferences preferences) {
this.bitcoinNetwork = preferences.getBitcoinNetwork();
switch (bitcoinNetwork) {
case TESTNET:

View File

@ -20,6 +20,7 @@ package io.bitsquare.btc;
import io.bitsquare.btc.exceptions.SigningException;
import io.bitsquare.btc.exceptions.TransactionVerificationException;
import io.bitsquare.btc.exceptions.WalletException;
import io.bitsquare.user.Preferences;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.AddressFormatException;
@ -91,8 +92,8 @@ public class TradeWalletService {
private final FeePolicy feePolicy;
@Inject
public TradeWalletService(BitcoinNetwork bitcoinNetwork, FeePolicy feePolicy) {
this.params = bitcoinNetwork.getParameters();
public TradeWalletService(Preferences preferences, FeePolicy feePolicy) {
this.params = preferences.getBitcoinNetwork().getParameters();
this.feePolicy = feePolicy;
}

View File

@ -21,6 +21,7 @@ import io.bitsquare.btc.listeners.AddressConfidenceListener;
import io.bitsquare.btc.listeners.BalanceListener;
import io.bitsquare.btc.listeners.TxConfidenceListener;
import io.bitsquare.crypto.CryptoService;
import io.bitsquare.user.Preferences;
import org.bitcoinj.core.AbstractWalletEventListener;
import org.bitcoinj.core.Address;
@ -127,15 +128,15 @@ public class WalletService {
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public WalletService(BitcoinNetwork bitcoinNetwork, RegTestHost regTestHost, CryptoService cryptoService,
public WalletService(RegTestHost regTestHost, CryptoService cryptoService,
TradeWalletService tradeWalletService, AddressEntryList addressEntryList, UserAgent userAgent,
@Named(DIR_KEY) File walletDir, @Named(PREFIX_KEY) String walletPrefix) {
@Named(DIR_KEY) File walletDir, @Named(PREFIX_KEY) String walletPrefix, Preferences preferences) {
this.regTestHost = regTestHost;
this.tradeWalletService = tradeWalletService;
this.addressEntryList = addressEntryList;
this.params = bitcoinNetwork.getParameters();
this.params = preferences.getBitcoinNetwork().getParameters();
this.cryptoService = cryptoService;
this.walletDir = walletDir;
this.walletDir = new File(walletDir, preferences.getBitcoinNetwork().toString().toLowerCase());
this.walletPrefix = walletPrefix;
this.userAgent = userAgent;
}

View File

@ -27,20 +27,19 @@ import org.slf4j.LoggerFactory;
public class BootstrapNodes {
private static final Logger log = LoggerFactory.getLogger(BootstrapNodes.class);
public static final int PORT = 7366;
public static final String DEFAULT_NODE_NAME = "default";
public static final int BASE_PORT = 7366; // port will be evaluated from btc network 7366 for mainnet, 7367 for testnet and 7368 for regtest
private static List<Node> bootstrapNodes = Arrays.asList(
Node.at(DEFAULT_NODE_NAME, "188.226.179.109", PORT),
Node.at(DEFAULT_NODE_NAME, "52.24.144.42", PORT),
Node.at(DEFAULT_NODE_NAME, "52.11.125.194", PORT)
//Node.at("digitalocean1.bitsquare.io", "188.226.179.109", BASE_PORT),
Node.at("aws1.bitsquare.io", "52.24.144.42", BASE_PORT),
Node.at("aws2.bitsquare.io", "52.11.125.194", BASE_PORT)
);
/**
* A locally-running BootstrapNode instance.
* Typically used only for testing. Not included in results from {@link #getAllBootstrapNodes()}.
*/
public static Node LOCALHOST = Node.at("localhost", "127.0.0.1", PORT);
public static Node LOCALHOST = Node.at("localhost", "127.0.0.1", BASE_PORT);
private static Node selectedNode = bootstrapNodes.get(new Random().nextInt(bootstrapNodes.size()));

View File

@ -94,12 +94,12 @@ public final class Node {
return port;
}
public PeerAddress toPeerAddress() {
public PeerAddress toPeerAddressWithPort(int port) {
try {
return new PeerAddress(Number160.createHash(getName()),
InetAddress.getByName(getIp()),
getPort(),
getPort());
port,
port);
} catch (UnknownHostException e) {
log.error("toPeerAddress failed: " + e.getMessage());
return null;

View File

@ -19,6 +19,7 @@ package io.bitsquare.p2p.tomp2p;
import io.bitsquare.p2p.BootstrapNodes;
import io.bitsquare.p2p.Node;
import io.bitsquare.user.Preferences;
import com.google.common.util.concurrent.SettableFuture;
@ -114,17 +115,17 @@ public class BootstrappedPeerBuilder {
}
}
private KeyPair keyPair;
private final int port;
private final boolean useManualPortForwarding;
private Node bootstrapNode;
private final String networkInterface;
private final Preferences preferences;
private final SettableFuture<PeerDHT> settableFuture = SettableFuture.create();
private final ObjectProperty<State> state = new SimpleObjectProperty<>(State.UNDEFINED);
private final ObjectProperty<ConnectionType> connectionType = new SimpleObjectProperty<>(ConnectionType.UNDEFINED);
private Node bootstrapNode;
private KeyPair keyPair;
private Peer peer;
private PeerDHT peerDHT;
private boolean retriedOtherBootstrapNode;
@ -139,13 +140,13 @@ public class BootstrappedPeerBuilder {
public BootstrappedPeerBuilder(@Named(Node.PORT_KEY) int port,
@Named(USE_MANUAL_PORT_FORWARDING_KEY) boolean useManualPortForwarding,
@Named(BOOTSTRAP_NODE_KEY) Node bootstrapNode,
@Named(NETWORK_INTERFACE_KEY) String networkInterface) {
@Named(NETWORK_INTERFACE_KEY) String networkInterface,
Preferences preferences) {
this.port = port;
this.useManualPortForwarding = useManualPortForwarding;
this.bootstrapNode = bootstrapNode;
this.networkInterface = networkInterface;
log.debug("Bootstrap to {}", bootstrapNode.toString());
this.preferences = preferences;
}
@ -168,6 +169,10 @@ public class BootstrappedPeerBuilder {
public SettableFuture<PeerDHT> start(int networkId) {
try {
// port is evaluated from btc network. 7366 for mainnet, 7367 for testnet and 7368 for regtest
bootstrapNode = Node.at(bootstrapNode.getName(), bootstrapNode.getIp(), bootstrapNode.getPort() + networkId);
log.debug("Bootstrap to {}", bootstrapNode.toString());
DefaultEventExecutorGroup eventExecutorGroup = new DefaultEventExecutorGroup(20);
ChannelClientConfiguration clientConf = PeerBuilder.createDefaultChannelClientConfiguration();
clientConf.pipelineFilter(new PeerBuilder.EventExecutorGroupFilter(eventExecutorGroup));
@ -219,8 +224,10 @@ public class BootstrappedPeerBuilder {
// log.debug("Peer updated: peerAddress=" + peerAddress + ", peerStatistics=" + peerStatistics);
}
});
discoverExternalAddress();
if (preferences.getUseUPnP())
discoverExternalAddressUsingUPnP();
else
discoverExternalAddress();
} catch (IOException e) {
handleError(State.PEER_CREATION_FAILED, "Cannot create a peer with port: " +
port + ". Exception: " + e);
@ -247,10 +254,11 @@ public class BootstrappedPeerBuilder {
// 4. If the port forwarding failed we can try as last resort to open a permanent TCP connection to the
// bootstrap node and use that peer as relay
private void discoverExternalAddress() {
private void discoverExternalAddressUsingUPnP() {
FutureDiscover futureDiscover = peer.discover().peerAddress(getBootstrapAddress()).start();
setState(State.DISCOVERY_STARTED);
PeerNAT peerNAT = new PeerBuilderNAT(peer).start();
FutureNAT futureNAT = peerNAT.startSetupPortforwarding(futureDiscover);
FutureRelayNAT futureRelayNAT = peerNAT.startRelay(new TCPRelayClientConfig(), futureDiscover, futureNAT);
@ -312,6 +320,62 @@ public class BootstrappedPeerBuilder {
});
}
private void discoverExternalAddress() {
FutureDiscover futureDiscover = peer.discover().peerAddress(getBootstrapAddress()).start();
setState(State.DISCOVERY_STARTED);
PeerNAT peerNAT = new PeerBuilderNAT(peer).start();
FutureRelayNAT futureRelayNAT = peerNAT.startRelay(new TCPRelayClientConfig(), futureDiscover);
futureRelayNAT.addListener(new BaseFutureListener<BaseFuture>() {
@Override
public void operationComplete(BaseFuture futureRelayNAT) throws Exception {
if (futureDiscover.isSuccess()) {
if (useManualPortForwarding) {
setState(State.DISCOVERY_MANUAL_PORT_FORWARDING_SUCCEEDED,
"NAT traversal successful with manual port forwarding.");
setConnectionType(ConnectionType.MANUAL_PORT_FORWARDING);
bootstrap();
}
else {
setState(State.DISCOVERY_DIRECT_SUCCEEDED, "Visible to the network. No NAT traversal needed.");
setConnectionType(ConnectionType.DIRECT);
bootstrap();
}
}
else {
if (futureRelayNAT.isSuccess()) {
// relay mode succeeded
setState(State.RELAY_SUCCEEDED, "Using relay mode.");
setConnectionType(ConnectionType.RELAY);
bootstrap();
}
else {
if (!retriedOtherBootstrapNode && BootstrapNodes.getAllBootstrapNodes().size() > 1) {
log.warn("Bootstrap failed with bootstrapNode: " + bootstrapNode + ". We try again with another node.");
retriedOtherBootstrapNode = true;
Optional<Node> optional = BootstrapNodes.getAllBootstrapNodes().stream().filter(e -> !e.equals(bootstrapNode)).findAny();
if (optional.isPresent()) {
bootstrapNode = optional.get();
executor.execute(() -> discoverExternalAddress());
}
}
else {
// All attempts failed. Give up...
handleError(State.RELAY_FAILED, "NAT traversal using relay mode failed " +
futureRelayNAT.failedReason());
}
}
}
}
@Override
public void exceptionCaught(Throwable t) throws Exception {
handleError(State.RELAY_FAILED, "Exception at bootstrap: " + t.getMessage());
}
});
}
private void bootstrap() {
log.trace("start bootstrap");

View File

@ -18,6 +18,7 @@
package io.bitsquare.user;
import io.bitsquare.app.Version;
import io.bitsquare.btc.BitcoinNetwork;
import io.bitsquare.storage.Storage;
import org.bitcoinj.utils.MonetaryFormat;
@ -37,6 +38,8 @@ import javafx.beans.property.StringProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
public class Preferences implements Serializable {
// That object is saved to disc. We need to take care of changes to not break deserialization.
private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
@ -45,7 +48,6 @@ public class Preferences implements Serializable {
// Deactivate mBit for now as most screens are not supporting it yet
transient private static final List<String> BTC_DENOMINATIONS = Arrays.asList(MonetaryFormat.CODE_BTC/*, MonetaryFormat.CODE_MBTC*/);
public static List<String> getBtcDenominations() {
return BTC_DENOMINATIONS;
}
@ -54,21 +56,25 @@ public class Preferences implements Serializable {
// Persisted fields
private String btcDenomination = MonetaryFormat.CODE_BTC;
private boolean useAnimations = true;
private boolean useEffects = true;
private boolean displaySecurityDepositInfo = true;
private boolean useUPnP = true;
private BitcoinNetwork bitcoinNetwork;
// Observable wrappers
transient private final StringProperty btcDenominationProperty = new SimpleStringProperty(btcDenomination);
transient private final BooleanProperty useAnimationsProperty = new SimpleBooleanProperty(useAnimations);
transient private final BooleanProperty useEffectsProperty = new SimpleBooleanProperty(useEffects);
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public Preferences(Storage<Preferences> storage) {
public Preferences(Storage<Preferences> storage, Environment environment) {
this.storage = storage;
Preferences persisted = storage.initAndGetPersisted(this);
@ -76,8 +82,13 @@ public class Preferences implements Serializable {
setBtcDenomination(persisted.btcDenomination);
setUseAnimations(persisted.useAnimations);
setUseEffects(persisted.useEffects);
setUseUPnP(persisted.useUPnP);
setBitcoinNetwork(persisted.bitcoinNetwork);
displaySecurityDepositInfo = persisted.getDisplaySecurityDepositInfo();
}
else {
setBitcoinNetwork(environment.getProperty(BitcoinNetwork.KEY, BitcoinNetwork.class, BitcoinNetwork.DEFAULT));
}
// Use that to guarantee update of the serializable field and to make a storage update in case of a change
btcDenominationProperty.addListener((ov) -> {
@ -116,6 +127,16 @@ public class Preferences implements Serializable {
storage.queueUpForSave();
}
public void setUseUPnP(boolean useUPnP) {
this.useUPnP = useUPnP;
storage.queueUpForSave();
}
public void setBitcoinNetwork(BitcoinNetwork bitcoinNetwork) {
this.bitcoinNetwork = bitcoinNetwork;
storage.queueUpForSave();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getter
@ -149,5 +170,11 @@ public class Preferences implements Serializable {
return useEffectsProperty;
}
public boolean getUseUPnP() {
return useUPnP;
}
public BitcoinNetwork getBitcoinNetwork() {
return bitcoinNetwork;
}
}

View File

@ -69,6 +69,8 @@ public class BitsquareApp extends Application {
private List<String> corruptedDatabaseFiles = new ArrayList<>();
private MainView mainView;
public static Runnable shutDownHandler;
public static void setEnvironment(Environment env) {
BitsquareApp.env = env;
}
@ -77,6 +79,8 @@ public class BitsquareApp extends Application {
public void start(Stage primaryStage) throws IOException {
this.primaryStage = primaryStage;
shutDownHandler = this::stop;
// setup UncaughtExceptionHandler
Thread.UncaughtExceptionHandler handler = (thread, throwable) -> {
// Might come from another thread

View File

@ -20,7 +20,6 @@ package io.bitsquare.gui.main;
import io.bitsquare.app.UpdateProcess;
import io.bitsquare.app.Version;
import io.bitsquare.arbitration.ArbitrationRepository;
import io.bitsquare.btc.BitcoinNetwork;
import io.bitsquare.btc.WalletService;
import io.bitsquare.crypto.KeyRing;
import io.bitsquare.fiat.FiatAccount;
@ -33,6 +32,7 @@ import io.bitsquare.p2p.tomp2p.BootstrappedPeerBuilder;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.TradeManager;
import io.bitsquare.trade.offer.OpenOfferManager;
import io.bitsquare.user.Preferences;
import io.bitsquare.user.User;
import io.bitsquare.util.Utilities;
@ -117,7 +117,7 @@ class MainViewModel implements ViewModel {
@Inject
public MainViewModel(User user, KeyRing keyRing, WalletService walletService, ArbitrationRepository arbitrationRepository, ClientNode clientNode,
TradeManager tradeManager, OpenOfferManager openOfferManager, BitcoinNetwork bitcoinNetwork, UpdateProcess updateProcess,
TradeManager tradeManager, OpenOfferManager openOfferManager, Preferences preferences, UpdateProcess updateProcess,
BSFormatter formatter) {
this.user = user;
this.keyRing = keyRing;
@ -129,8 +129,8 @@ class MainViewModel implements ViewModel {
this.updateProcess = updateProcess;
this.formatter = formatter;
bitcoinNetworkAsString = formatter.formatBitcoinNetwork(bitcoinNetwork);
networkId = bitcoinNetwork.ordinal();
bitcoinNetworkAsString = formatter.formatBitcoinNetwork(preferences.getBitcoinNetwork());
networkId = preferences.getBitcoinNetwork().ordinal();
updateProcess.state.addListener((observableValue, oldValue, newValue) -> applyUpdateState(newValue));
applyUpdateState(updateProcess.state.get());

View File

@ -31,7 +31,7 @@
<Insets bottom="10.0" left="25.0" top="30.0" right="25"/>
</padding>
<TitledGroupBg text="Network information" GridPane.rowSpan="8"/>
<TitledGroupBg text="Bitcoin network" GridPane.rowSpan="3"/>
<Label text="Bitcoin network type:" GridPane.rowIndex="0">
<GridPane.margin>
@ -45,46 +45,54 @@
</GridPane.margin>
</TextField>
<Label text="Bitcoin network connected peers:" GridPane.rowIndex="1"/>
<TextField fx:id="connectedPeersBTC" GridPane.rowIndex="1" GridPane.columnIndex="1"
<Label text="Select Network:" GridPane.rowIndex="1"/>
<ComboBox fx:id="netWorkComboBox" onAction="#onSelectNetwork" GridPane.rowIndex="1" GridPane.columnIndex="1"/>
<Label text="Bitcoin network connected peers:" GridPane.rowIndex="2"/>
<TextField fx:id="connectedPeersBTC" GridPane.rowIndex="2" GridPane.columnIndex="1"
mouseTransparent="true" focusTraversable="false"/>
<Label text="P2P network connection:" GridPane.rowIndex="2"/>
<TextField fx:id="connectionType" GridPane.rowIndex="2" GridPane.columnIndex="1"
mouseTransparent="true" focusTraversable="false"/>
<Label text="P2P network connected peers:" GridPane.rowIndex="3"/>
<TextField fx:id="connectedPeersP2P" GridPane.rowIndex="3" GridPane.columnIndex="1"
mouseTransparent="true" focusTraversable="false"/>
<Label text="My external visible P2P network address:" GridPane.rowIndex="4"/>
<TextField fx:id="nodeAddress" GridPane.rowIndex="4" GridPane.columnIndex="1"
mouseTransparent="true" focusTraversable="false"/>
<Label text="P2P bootstrap node address:" GridPane.rowIndex="5">
<TitledGroupBg text="P2P network" GridPane.rowIndex="3" GridPane.rowSpan="5">
<padding>
<Insets top="50.0"/>
</padding>
<GridPane.margin>
<Insets bottom="-15"/>
<Insets bottom="-10" left="-10" right="-10" top="30"/>
</GridPane.margin>
</TitledGroupBg>
<Label text="P2P network connection:" GridPane.rowIndex="3">
<GridPane.margin>
<Insets top="50.0"/>
</GridPane.margin>
</Label>
<TextField fx:id="bootstrapNodeAddress" GridPane.rowIndex="5" GridPane.columnIndex="1"
<TextField fx:id="connectionType" GridPane.rowIndex="3" GridPane.columnIndex="1"
mouseTransparent="true" focusTraversable="false">
<GridPane.margin>
<Insets bottom="-15"/>
<Insets top="50.0"/>
</GridPane.margin>
</TextField>
<Label text="Use UPnP:" GridPane.rowIndex="4"/>
<CheckBox fx:id="useUPnP" onAction="#onSelectUPnP" GridPane.rowIndex="4" GridPane.columnIndex="1"/>
<Label text="P2P network connected peers:" GridPane.rowIndex="5"/>
<TextField fx:id="connectedPeersP2P" GridPane.rowIndex="5" GridPane.columnIndex="1"
mouseTransparent="true" focusTraversable="false"/>
<Label text="My external visible P2P network address:" GridPane.rowIndex="6"/>
<TextField fx:id="nodeAddress" GridPane.rowIndex="6" GridPane.columnIndex="1"
mouseTransparent="true" focusTraversable="false"/>
<Label text="P2P bootstrap node address:" GridPane.rowIndex="7"/>
<TextField fx:id="bootstrapNodeAddress" GridPane.rowIndex="7" GridPane.columnIndex="1"
mouseTransparent="true" focusTraversable="false"/>
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" halignment="RIGHT" minWidth="200.0"/>
<ColumnConstraints hgrow="ALWAYS" minWidth="300.0"/>
</columnConstraints>
<rowConstraints>
<RowConstraints vgrow="NEVER"/>
<RowConstraints vgrow="NEVER"/>
<RowConstraints vgrow="NEVER"/>
</rowConstraints>
</GridPane>

View File

@ -17,18 +17,30 @@
package io.bitsquare.gui.main.settings.network;
import io.bitsquare.app.BitsquareApp;
import io.bitsquare.btc.BitcoinNetwork;
import io.bitsquare.btc.WalletService;
import io.bitsquare.gui.common.view.FxmlView;
import io.bitsquare.gui.common.view.InitializableView;
import io.bitsquare.gui.components.Popups;
import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.locale.BSResources;
import io.bitsquare.p2p.ClientNode;
import io.bitsquare.user.Preferences;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import org.controlsfx.control.action.AbstractAction;
import org.controlsfx.control.action.Action;
import static javafx.beans.binding.Bindings.createStringBinding;
@FxmlView
@ -36,14 +48,19 @@ public class NetworkSettingsView extends InitializableView {
private final String bitcoinNetworkString;
private final WalletService walletService;
private final Preferences preferences;
private final ClientNode clientNode;
@FXML TextField bitcoinNetwork, connectionType, nodeAddress, bootstrapNodeAddress, connectedPeersBTC, connectedPeersP2P;
@FXML CheckBox useUPnP;
@FXML ComboBox<BitcoinNetwork> netWorkComboBox;
@Inject
public NetworkSettingsView(BitcoinNetwork bitcoinNetwork, WalletService walletService, ClientNode clientNode, BSFormatter formatter) {
public NetworkSettingsView(WalletService walletService, ClientNode clientNode, Preferences preferences, BSFormatter
formatter) {
this.walletService = walletService;
this.bitcoinNetworkString = formatter.formatBitcoinNetwork(bitcoinNetwork);
this.preferences = preferences;
this.bitcoinNetworkString = formatter.formatBitcoinNetwork(preferences.getBitcoinNetwork());
this.clientNode = clientNode;
}
@ -56,6 +73,46 @@ public class NetworkSettingsView extends InitializableView {
connectedPeersP2P.textProperty().bind(createStringBinding(() -> String.valueOf(clientNode.numPeersProperty().get()), clientNode.numPeersProperty()));
nodeAddress.setText(clientNode.getAddress().toString());
bootstrapNodeAddress.setText(clientNode.getBootstrapNodeAddress().toString());
useUPnP.setSelected(preferences.getUseUPnP());
netWorkComboBox.setItems(FXCollections.observableArrayList(BitcoinNetwork.values()));
netWorkComboBox.getSelectionModel().select(preferences.getBitcoinNetwork());
}
@FXML
void onSelectUPnP() {
preferences.setUseUPnP(useUPnP.isSelected());
}
@FXML
void onSelectNetwork() {
preferences.setBitcoinNetwork(netWorkComboBox.getSelectionModel().getSelectedItem());
List<Action> actions = new ArrayList<>();
actions.add(new AbstractAction(BSResources.get("shared.no")) {
@Override
public void handle(ActionEvent actionEvent) {
getProperties().put("type", "NO");
org.controlsfx.dialog.Dialog.Actions.NO.handle(actionEvent);
}
});
actions.add(new AbstractAction(BSResources.get("shared.yes")) {
@Override
public void handle(ActionEvent actionEvent) {
getProperties().put("type", "YES");
org.controlsfx.dialog.Dialog.Actions.YES.handle(actionEvent);
}
});
Action response = Popups.openConfirmPopup("Info", null,
"You need to restart the application to apply the change of the Bitcoin network." +
"\n\nDo you want to shutdown now?",
actions);
if (Popups.isYes(response))
BitsquareApp.shutDownHandler.run();
}
}

View File

@ -17,7 +17,7 @@
package io.bitsquare.gui.util.validation;
import io.bitsquare.btc.BitcoinNetwork;
import io.bitsquare.user.Preferences;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.AddressFormatException;
@ -26,11 +26,11 @@ import javax.inject.Inject;
public final class BtcAddressValidator extends InputValidator {
private final BitcoinNetwork bitcoinNetwork;
private Preferences preferences;
@Inject
public BtcAddressValidator(BitcoinNetwork bitcoinNetwork) {
this.bitcoinNetwork = bitcoinNetwork;
public BtcAddressValidator(Preferences preferences) {
this.preferences = preferences;
}
@Override
@ -45,7 +45,7 @@ public final class BtcAddressValidator extends InputValidator {
private ValidationResult validateBtcAddress(String input) {
try {
new Address(bitcoinNetwork.getParameters(), input);
new Address(preferences.getBitcoinNetwork().getParameters(), input);
return new ValidationResult(true);
} catch (AddressFormatException e) {
return new ValidationResult(false, "Bitcoin address is a valid format");