Add new bootstrap node servers

This commit is contained in:
Manfred Karrer 2015-05-10 09:17:09 +02:00
parent 44c8cf9c26
commit c1e0524090
18 changed files with 231 additions and 115 deletions

View file

@ -21,9 +21,12 @@ import io.bitsquare.app.Logging;
import io.bitsquare.p2p.BootstrapNodes; import io.bitsquare.p2p.BootstrapNodes;
import io.bitsquare.p2p.Node; import io.bitsquare.p2p.Node;
import net.tomp2p.connection.ChannelClientConfiguration; import java.util.Collection;
import net.tomp2p.connection.ChannelServerConfiguration; import java.util.Random;
import java.util.stream.Collectors;
import net.tomp2p.dht.PeerBuilderDHT; import net.tomp2p.dht.PeerBuilderDHT;
import net.tomp2p.dht.PeerDHT;
import net.tomp2p.nat.PeerBuilderNAT; import net.tomp2p.nat.PeerBuilderNAT;
import net.tomp2p.p2p.Peer; import net.tomp2p.p2p.Peer;
import net.tomp2p.p2p.PeerBuilder; import net.tomp2p.p2p.PeerBuilder;
@ -35,7 +38,6 @@ import net.tomp2p.peers.PeerStatistic;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
public class BootstrapNode { public class BootstrapNode {
@ -52,26 +54,27 @@ public class BootstrapNode {
this.env = env; this.env = env;
} }
public void start() { public void start() {
int port = env.getProperty(Node.PORT_KEY, Integer.class, BootstrapNodes.DEFAULT_PORT); int port = env.getProperty(Node.PORT_KEY, Integer.class, BootstrapNodes.PORT);
String name = env.getRequiredProperty(Node.NAME_KEY); String name = env.getProperty(Node.NAME_KEY, BootstrapNodes.DEFAULT_NODE_NAME);
Logging.setup(name + "_" + port); Logging.setup(name + "_" + port);
try { try {
Number160 peerId = Number160.createHash(name); Number160 peerId = Number160.createHash(new Random().nextInt());
/*
DefaultEventExecutorGroup eventExecutorGroup = new DefaultEventExecutorGroup(50); DefaultEventExecutorGroup eventExecutorGroup = new DefaultEventExecutorGroup(50);
ChannelClientConfiguration clientConf = PeerBuilder.createDefaultChannelClientConfiguration(); ChannelClientConfiguration clientConf = PeerBuilder.createDefaultChannelClientConfiguration();
clientConf.pipelineFilter(new PeerBuilder.EventExecutorGroupFilter(eventExecutorGroup)); clientConf.pipelineFilter(new PeerBuilder.EventExecutorGroupFilter(eventExecutorGroup));
ChannelServerConfiguration serverConf = PeerBuilder.createDefaultChannelServerConfiguration(); ChannelServerConfiguration serverConf = PeerBuilder.createDefaultChannelServerConfiguration();
serverConf.pipelineFilter(new PeerBuilder.EventExecutorGroupFilter(eventExecutorGroup)); serverConf.pipelineFilter(new PeerBuilder.EventExecutorGroupFilter(eventExecutorGroup));
serverConf.connectionTimeoutTCPMillis(5000); serverConf.connectionTimeoutTCPMillis(5000);*/
peer = new PeerBuilder(peerId) peer = new PeerBuilder(peerId)
.ports(port) .ports(port)
.channelClientConfiguration(clientConf) /* .channelClientConfiguration(clientConf)
.channelServerConfiguration(serverConf) .channelServerConfiguration(serverConf)*/
.start(); .start();
/*peer.objectDataReply((sender, request) -> { /*peer.objectDataReply((sender, request) -> {
@ -79,14 +82,25 @@ public class BootstrapNode {
return "pong"; return "pong";
});*/ });*/
new PeerBuilderDHT(peer).start(); PeerDHT peerDHT = new PeerBuilderDHT(peer).start();
new PeerBuilderNAT(peer).start(); new PeerBuilderNAT(peer).start();
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());
log.info("Bootstrapping to " + bootstrapNodes.size() + " bootstrapNode(s)");
log.info("Bootstrapping bootstrapNodes " + bootstrapNodes);
peer.bootstrap().bootstrapTo(bootstrapNodes).start().awaitUninterruptibly();
}
else {
log.info("Localhost, no bootstrap");
}
peer.peerBean().peerMap().addPeerMapChangeListener(new PeerMapChangeListener() { peer.peerBean().peerMap().addPeerMapChangeListener(new PeerMapChangeListener() {
@Override @Override
public void peerInserted(PeerAddress peerAddress, boolean verified) { public void peerInserted(PeerAddress peerAddress, boolean verified) {
try { try {
log.debug("Peer inserted: peerAddress=" + peerAddress + ", verified=" + verified); log.info("Peer inserted: peerAddress=" + peerAddress + ", verified=" + verified);
} catch (Throwable t) { } catch (Throwable t) {
log.error("Exception at peerInserted " + t.getMessage()); log.error("Exception at peerInserted " + t.getMessage());
} }
@ -95,7 +109,7 @@ public class BootstrapNode {
@Override @Override
public void peerRemoved(PeerAddress peerAddress, PeerStatistic peerStatistics) { public void peerRemoved(PeerAddress peerAddress, PeerStatistic peerStatistics) {
try { try {
log.debug("Peer removed: peerAddress=" + peerAddress + ", peerStatistics=" + peerStatistics); log.info("Peer removed: peerAddress=" + peerAddress + ", peerStatistics=" + peerStatistics);
} catch (Throwable t) { } catch (Throwable t) {
log.error("Exception at peerRemoved " + t.getMessage()); log.error("Exception at peerRemoved " + t.getMessage());
} }
@ -104,7 +118,7 @@ public class BootstrapNode {
@Override @Override
public void peerUpdated(PeerAddress peerAddress, PeerStatistic peerStatistics) { public void peerUpdated(PeerAddress peerAddress, PeerStatistic peerStatistics) {
try { try {
//log.debug("Peer updated: peerAddress=" + peerAddress + ", peerStatistics=" + peerStatistics); log.info("Peer updated: peerAddress=" + peerAddress + ", peerStatistics=" + peerStatistics);
} catch (Throwable t) { } catch (Throwable t) {
log.error("Exception at peerUpdated " + t.getMessage()); log.error("Exception at peerUpdated " + t.getMessage());
} }
@ -130,7 +144,7 @@ public class BootstrapNode {
noPeersInfoPrinted = true; noPeersInfoPrinted = true;
} }
try { try {
Thread.sleep(10000); Thread.sleep(2000);
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
return; return;

View file

@ -32,10 +32,9 @@ public class BootstrapNodeMain extends BitsquareExecutable {
} }
protected void customizeOptionParsing(OptionParser parser) { protected void customizeOptionParsing(OptionParser parser) {
parser.accepts(Node.NAME_KEY, description("Name of this node", null)) parser.accepts(Node.NAME_KEY, description("Name of this node", BootstrapNodes.DEFAULT_NODE_NAME))
.withRequiredArg() .withRequiredArg();
.isRequired(); parser.accepts(Node.PORT_KEY, description("Port to listen on", BootstrapNodes.PORT))
parser.accepts(Node.PORT_KEY, description("Port to listen on", BootstrapNodes.DEFAULT_PORT))
.withRequiredArg() .withRequiredArg()
.ofType(int.class); .ofType(int.class);
} }

View file

@ -43,7 +43,6 @@ public abstract class BitsquareModule extends AbstractModule {
protected void install(BitsquareModule module) { protected void install(BitsquareModule module) {
super.install(module); super.install(module);
log.trace("install " + module.getClass().getSimpleName());
modules.add(module); modules.add(module);
} }
@ -68,6 +67,5 @@ public abstract class BitsquareModule extends AbstractModule {
* @param injector the Injector originally initialized with this module * @param injector the Injector originally initialized with this module
*/ */
protected void doClose(Injector injector) { protected void doClose(Injector injector) {
log.trace("doClose " + getClass().getSimpleName());
} }
} }

View file

@ -19,7 +19,6 @@ package io.bitsquare.app;
import io.bitsquare.BitsquareException; import io.bitsquare.BitsquareException;
import io.bitsquare.btc.BitcoinNetwork; import io.bitsquare.btc.BitcoinNetwork;
import io.bitsquare.btc.RegTestHost;
import io.bitsquare.btc.UserAgent; import io.bitsquare.btc.UserAgent;
import io.bitsquare.btc.WalletService; import io.bitsquare.btc.WalletService;
import io.bitsquare.crypto.KeyStorage; import io.bitsquare.crypto.KeyStorage;
@ -93,13 +92,8 @@ public class BitsquareEnvironment extends StandardEnvironment {
(String) commandLineProperties.getProperty(BitcoinNetwork.KEY) : (String) commandLineProperties.getProperty(BitcoinNetwork.KEY) :
BitcoinNetwork.DEFAULT.toString(); BitcoinNetwork.DEFAULT.toString();
String regTestHost = commandLineProperties.containsProperty(RegTestHost.KEY) ?
(String) commandLineProperties.getProperty(RegTestHost.KEY) :
RegTestHost.DEFAULT.toString();
this.bootstrapNodePort = commandLineProperties.containsProperty(TomP2PModule.BOOTSTRAP_NODE_PORT_KEY) ? this.bootstrapNodePort = commandLineProperties.containsProperty(TomP2PModule.BOOTSTRAP_NODE_PORT_KEY) ?
(String) commandLineProperties.getProperty(TomP2PModule.BOOTSTRAP_NODE_PORT_KEY) : (String) commandLineProperties.getProperty(TomP2PModule.BOOTSTRAP_NODE_PORT_KEY) : String.valueOf(BootstrapNodes.PORT);
getBootstrapNodePort(BitcoinNetwork.valueOf(bitcoinNetwork), RegTestHost.valueOf(regTestHost));
MutablePropertySources propertySources = this.getPropertySources(); MutablePropertySources propertySources = this.getPropertySources();
propertySources.addFirst(commandLineProperties); propertySources.addFirst(commandLineProperties);
@ -113,19 +107,6 @@ public class BitsquareEnvironment extends StandardEnvironment {
} }
} }
private String getBootstrapNodePort(BitcoinNetwork bitcoinNetwork, RegTestHost regTestHost) {
// We use default port 7366 for mainnet, 7367 for testnet and 7368 for regtest
if (bitcoinNetwork == BitcoinNetwork.REGTEST && regTestHost == RegTestHost.DIGITAL_OCEAN_1) {
return String.valueOf(BootstrapNodes.DEFAULT_PORT + 2);
}
else if (bitcoinNetwork == BitcoinNetwork.TESTNET) {
return String.valueOf(BootstrapNodes.DEFAULT_PORT + 1);
}
else {
return String.valueOf(BootstrapNodes.DEFAULT_PORT);
}
}
PropertySource<?> appDirProperties() throws Exception { PropertySource<?> appDirProperties() throws Exception {
String location = String.format("file:%s/bitsquare.properties", appDataDir); String location = String.format("file:%s/bitsquare.properties", appDataDir);
Resource resource = resourceLoader.getResource(location); Resource resource = resourceLoader.getResource(location);

View file

@ -29,7 +29,7 @@ public enum BitcoinNetwork {
REGTEST(RegTestParams.get()); REGTEST(RegTestParams.get());
public static final String KEY = "bitcoin.network"; public static final String KEY = "bitcoin.network";
public static final BitcoinNetwork DEFAULT = REGTEST; public static final BitcoinNetwork DEFAULT = TESTNET;
private final NetworkParameters parameters; private final NetworkParameters parameters;

View file

@ -19,9 +19,11 @@ package io.bitsquare.btc;
public enum RegTestHost { public enum RegTestHost {
LOCALHOST(), LOCALHOST,
DIGITAL_OCEAN_1(); // 188.226.179.109 REG_TEST_SERVER; // 188.226.179.109
public static final String KEY = "bitcoin.regtest.host"; public static final String KEY = "bitcoin.regtest.host";
public static final RegTestHost DEFAULT = DIGITAL_OCEAN_1; public static final RegTestHost DEFAULT = LOCALHOST;
public static final String SERVER_IP = "188.226.179.109";
} }

View file

@ -106,7 +106,7 @@ public class WalletService {
private final DownloadListener downloadListener = new DownloadListener(); private final DownloadListener downloadListener = new DownloadListener();
private final WalletEventListener walletEventListener = new BitsquareWalletEventListener(); private final WalletEventListener walletEventListener = new BitsquareWalletEventListener();
private RegTestHost regTestHost; private final RegTestHost regTestHost;
private final TradeWalletService tradeWalletService; private final TradeWalletService tradeWalletService;
private final AddressEntryList addressEntryList; private final AddressEntryList addressEntryList;
private final NetworkParameters params; private final NetworkParameters params;
@ -175,9 +175,9 @@ public class WalletService {
// Now configure and start the appkit. This will take a second or two - we could show a temporary splash screen // Now configure and start the appkit. This will take a second or two - we could show a temporary splash screen
// or progress widget to keep the user engaged whilst we initialise, but we don't. // or progress widget to keep the user engaged whilst we initialise, but we don't.
if (params == RegTestParams.get()) { if (params == RegTestParams.get()) {
if (regTestHost == RegTestHost.DIGITAL_OCEAN_1) { if (regTestHost == RegTestHost.REG_TEST_SERVER) {
try { try {
walletAppKit.setPeerNodes(new PeerAddress(InetAddress.getByName("188.226.179.109"), params.getPort())); walletAppKit.setPeerNodes(new PeerAddress(InetAddress.getByName(RegTestHost.SERVER_IP), params.getPort()));
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View file

@ -19,30 +19,36 @@ package io.bitsquare.p2p;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Random;
public interface BootstrapNodes { import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
int DEFAULT_PORT = 7366; public class BootstrapNodes {
private static final Logger log = LoggerFactory.getLogger(BootstrapNodes.class);
Node DIGITAL_OCEAN_1 = Node.at("digitalocean1.bitsquare.io", "188.226.179.109", DEFAULT_PORT); public static final int PORT = 7366;
public static final String DEFAULT_NODE_NAME = "default";
/** private static List<Node> bootstrapNodes = Arrays.asList(
* Alias to the default bootstrap node. Node.at(DEFAULT_NODE_NAME, "188.226.179.109", PORT),
*/ Node.at(DEFAULT_NODE_NAME, "52.24.144.42", PORT),
Node DEFAULT = DIGITAL_OCEAN_1; Node.at(DEFAULT_NODE_NAME, "52.11.125.194", PORT)
);
/** /**
* A locally-running BootstrapNode instance. * A locally-running BootstrapNode instance.
* Typically used only for testing. Not included in results from {@link #all()}. * Typically used only for testing. Not included in results from {@link #getAllBootstrapNodes()}.
*/ */
Node LOCALHOST = Node.at("localhost", "127.0.0.1", DEFAULT_PORT); public static Node LOCALHOST = Node.at("localhost", "127.0.0.1", PORT);
/** private static Node selectedNode = bootstrapNodes.get(new Random().nextInt(bootstrapNodes.size()));
* All known public bootstrap nodes.
*/ public static List<Node> getAllBootstrapNodes() {
static List<Node> all() { return bootstrapNodes;
return Arrays.asList( }
DIGITAL_OCEAN_1
); public static Node getSelectedNode() {
return selectedNode;
} }
} }

View file

@ -21,6 +21,8 @@ import io.bitsquare.p2p.tomp2p.BootstrappedPeerBuilder;
import java.security.KeyPair; import java.security.KeyPair;
import java.util.concurrent.Executor;
import javafx.beans.property.ReadOnlyIntegerProperty; import javafx.beans.property.ReadOnlyIntegerProperty;
import rx.Observable; import rx.Observable;
@ -32,7 +34,9 @@ public interface ClientNode {
Node getBootstrapNodeAddress(); Node getBootstrapNodeAddress();
Observable<BootstrappedPeerBuilder.State> bootstrap(KeyPair keyPair); Observable<BootstrappedPeerBuilder.State> bootstrap(int networkId, KeyPair keyPair);
ReadOnlyIntegerProperty numPeersProperty(); ReadOnlyIntegerProperty numPeersProperty();
void setExecutor(Executor executor);
} }

View file

@ -21,7 +21,12 @@ import com.google.common.base.Objects;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.UnknownHostException;
import net.tomp2p.peers.Number160;
import net.tomp2p.peers.PeerAddress;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -89,6 +94,18 @@ public final class Node {
return port; return port;
} }
public PeerAddress toPeerAddress() {
try {
return new PeerAddress(Number160.createHash(getName()),
InetAddress.getByName(getIp()),
getPort(),
getPort());
} catch (UnknownHostException e) {
log.error("toPeerAddress failed: " + e.getMessage());
return null;
}
}
@Override @Override
public boolean equals(Object object) { public boolean equals(Object object) {
if (this == object) if (this == object)

View file

@ -17,6 +17,7 @@
package io.bitsquare.p2p.tomp2p; package io.bitsquare.p2p.tomp2p;
import io.bitsquare.p2p.BootstrapNodes;
import io.bitsquare.p2p.Node; import io.bitsquare.p2p.Node;
import com.google.common.util.concurrent.SettableFuture; import com.google.common.util.concurrent.SettableFuture;
@ -30,6 +31,9 @@ import java.net.UnknownHostException;
import java.security.KeyPair; import java.security.KeyPair;
import java.util.Optional;
import java.util.concurrent.Executor;
import javax.inject.Inject; import javax.inject.Inject;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectProperty;
@ -113,7 +117,7 @@ public class BootstrappedPeerBuilder {
private KeyPair keyPair; private KeyPair keyPair;
private final int port; private final int port;
private final boolean useManualPortForwarding; private final boolean useManualPortForwarding;
private final Node bootstrapNode; private Node bootstrapNode;
private final String networkInterface; private final String networkInterface;
private final SettableFuture<PeerDHT> settableFuture = SettableFuture.create(); private final SettableFuture<PeerDHT> settableFuture = SettableFuture.create();
@ -123,6 +127,8 @@ public class BootstrappedPeerBuilder {
private Peer peer; private Peer peer;
private PeerDHT peerDHT; private PeerDHT peerDHT;
private boolean retriedOtherBootstrapNode;
private Executor executor;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -156,7 +162,11 @@ public class BootstrappedPeerBuilder {
// Public methods // Public methods
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public SettableFuture<PeerDHT> start() { public void setExecutor(Executor executor) {
this.executor = executor;
}
public SettableFuture<PeerDHT> start(int networkId) {
try { try {
DefaultEventExecutorGroup eventExecutorGroup = new DefaultEventExecutorGroup(20); DefaultEventExecutorGroup eventExecutorGroup = new DefaultEventExecutorGroup(20);
ChannelClientConfiguration clientConf = PeerBuilder.createDefaultChannelClientConfiguration(); ChannelClientConfiguration clientConf = PeerBuilder.createDefaultChannelClientConfiguration();
@ -172,6 +182,7 @@ public class BootstrappedPeerBuilder {
if (useManualPortForwarding) { if (useManualPortForwarding) {
peer = new PeerBuilder(keyPair) peer = new PeerBuilder(keyPair)
.p2pId(networkId)
.channelClientConfiguration(clientConf) .channelClientConfiguration(clientConf)
.channelServerConfiguration(serverConf) .channelServerConfiguration(serverConf)
.ports(port) .ports(port)
@ -182,6 +193,7 @@ public class BootstrappedPeerBuilder {
} }
else { else {
peer = new PeerBuilder(keyPair) peer = new PeerBuilder(keyPair)
.p2pId(networkId)
.channelClientConfiguration(clientConf) .channelClientConfiguration(clientConf)
.channelServerConfiguration(serverConf) .channelServerConfiguration(serverConf)
.ports(port) .ports(port)
@ -274,9 +286,20 @@ public class BootstrappedPeerBuilder {
bootstrap(); bootstrap();
} }
else { else {
// All attempts failed. Give up... if (!retriedOtherBootstrapNode && BootstrapNodes.getAllBootstrapNodes().size() > 1) {
handleError(State.RELAY_FAILED, "NAT traversal using relay mode failed " + log.warn("Bootstrap failed with bootstrapNode: " + bootstrapNode + ". We try again with another node.");
futureRelayNAT.failedReason()); 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());
}
} }
} }
} }

View file

@ -65,9 +65,9 @@ public class TomP2PModule extends P2PModule {
env.getProperty(USE_MANUAL_PORT_FORWARDING_KEY, boolean.class, false)); env.getProperty(USE_MANUAL_PORT_FORWARDING_KEY, boolean.class, false));
bind(Node.class).annotatedWith(Names.named(BOOTSTRAP_NODE_KEY)).toInstance( bind(Node.class).annotatedWith(Names.named(BOOTSTRAP_NODE_KEY)).toInstance(
Node.at(env.getProperty(BOOTSTRAP_NODE_NAME_KEY, BootstrapNodes.DEFAULT.getName()), Node.at(env.getProperty(BOOTSTRAP_NODE_NAME_KEY, BootstrapNodes.getSelectedNode().getName()),
env.getProperty(BOOTSTRAP_NODE_IP_KEY, BootstrapNodes.DEFAULT.getIp()), env.getProperty(BOOTSTRAP_NODE_IP_KEY, BootstrapNodes.getSelectedNode().getIp()),
env.getProperty(BOOTSTRAP_NODE_PORT_KEY, int.class, BootstrapNodes.DEFAULT.getPort()) env.getProperty(BOOTSTRAP_NODE_PORT_KEY, int.class, BootstrapNodes.getSelectedNode().getPort())
) )
); );
bindConstant().annotatedWith(Names.named(NETWORK_INTERFACE_KEY)).to(env.getProperty(NETWORK_INTERFACE_KEY, NETWORK_INTERFACE_UNSPECIFIED)); bindConstant().annotatedWith(Names.named(NETWORK_INTERFACE_KEY)).to(env.getProperty(NETWORK_INTERFACE_KEY, NETWORK_INTERFACE_UNSPECIFIED));

View file

@ -31,6 +31,7 @@ import java.security.KeyPair;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -40,12 +41,10 @@ import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ReadOnlyIntegerProperty; import javafx.beans.property.ReadOnlyIntegerProperty;
import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleIntegerProperty;
import net.tomp2p.connection.PeerConnection;
import net.tomp2p.connection.PeerException;
import net.tomp2p.dht.PeerDHT; import net.tomp2p.dht.PeerDHT;
import net.tomp2p.peers.PeerAddress; import net.tomp2p.peers.PeerAddress;
import net.tomp2p.peers.PeerStatusListener; import net.tomp2p.peers.PeerMapChangeListener;
import net.tomp2p.peers.RTT; import net.tomp2p.peers.PeerStatistic;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -88,7 +87,11 @@ public class TomP2PNode implements ClientNode {
// Public methods // Public methods
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public Observable<BootstrappedPeerBuilder.State> bootstrap(KeyPair keyPair) { public void setExecutor(Executor executor) {
bootstrappedPeerBuilder.setExecutor(executor);
}
public Observable<BootstrappedPeerBuilder.State> bootstrap(int networkId, KeyPair keyPair) {
bootstrappedPeerBuilder.setKeyPair(keyPair); bootstrappedPeerBuilder.setKeyPair(keyPair);
bootstrappedPeerBuilder.getState().addListener((ov, oldValue, newValue) -> { bootstrappedPeerBuilder.getState().addListener((ov, oldValue, newValue) -> {
@ -96,14 +99,39 @@ public class TomP2PNode implements ClientNode {
bootstrapStateSubject.onNext(newValue); bootstrapStateSubject.onNext(newValue);
}); });
SettableFuture<PeerDHT> bootstrapFuture = bootstrappedPeerBuilder.start(); SettableFuture<PeerDHT> bootstrapFuture = bootstrappedPeerBuilder.start(networkId);
Futures.addCallback(bootstrapFuture, new FutureCallback<PeerDHT>() { Futures.addCallback(bootstrapFuture, new FutureCallback<PeerDHT>() {
@Override @Override
public void onSuccess(@Nullable PeerDHT peerDHT) { public void onSuccess(@Nullable PeerDHT peerDHT) {
if (peerDHT != null) { if (peerDHT != null) {
TomP2PNode.this.peerDHT = peerDHT; TomP2PNode.this.peerDHT = peerDHT;
peerDHT.peerBean().addPeerStatusListener(new PeerStatusListener() { BaseP2PService.getUserThread().execute(() -> numPeers.set(peerDHT.peerBean().peerMap().all().size()));
log.debug("Number of peers = " + peerDHT.peerBean().peerMap().all().size());
peerDHT.peerBean().peerMap().addPeerMapChangeListener(new PeerMapChangeListener() {
@Override
public void peerInserted(PeerAddress peerAddress, boolean b) {
BaseP2PService.getUserThread().execute(() -> numPeers.set(peerDHT.peerBean().peerMap().all().size()));
log.debug("peerInserted " + peerAddress);
log.debug("Number of peers = " + peerDHT.peerBean().peerMap().all().size());
}
@Override
public void peerRemoved(PeerAddress peerAddress, PeerStatistic peerStatistic) {
BaseP2PService.getUserThread().execute(() -> numPeers.set(peerDHT.peerBean().peerMap().all().size()));
log.debug("peerRemoved " + peerAddress);
log.debug("Number of peers = " + peerDHT.peerBean().peerMap().all().size());
}
@Override
public void peerUpdated(PeerAddress peerAddress, PeerStatistic peerStatistic) {
BaseP2PService.getUserThread().execute(() -> numPeers.set(peerDHT.peerBean().peerMap().all().size()));
log.debug("peerUpdated " + peerAddress);
log.debug("Number of peers = " + peerDHT.peerBean().peerMap().all().size());
}
});
/* peerDHT.peerBean().addPeerStatusListener(new PeerStatusListener() {
@Override @Override
public boolean peerFailed(PeerAddress peerAddress, PeerException e) { public boolean peerFailed(PeerAddress peerAddress, PeerException e) {
return false; return false;
@ -114,7 +142,7 @@ public class TomP2PNode implements ClientNode {
BaseP2PService.getUserThread().execute(() -> numPeers.set(peerDHT.peerBean().peerMap().size())); BaseP2PService.getUserThread().execute(() -> numPeers.set(peerDHT.peerBean().peerMap().size()));
return false; return false;
} }
}); });*/
resultHandlers.stream().forEach(ResultHandler::handleResult); resultHandlers.stream().forEach(ResultHandler::handleResult);
bootstrapStateSubject.onCompleted(); bootstrapStateSubject.onCompleted();

View file

@ -134,11 +134,11 @@ public class BitsquareAppMain extends BitsquareExecutable {
.withValuesConvertedBy(new EnumValueConverter(RegTestHost.class)); .withValuesConvertedBy(new EnumValueConverter(RegTestHost.class));
parser.accepts(BOOTSTRAP_NODE_NAME_KEY, description("", BootstrapNodes.DEFAULT.getName())) parser.accepts(BOOTSTRAP_NODE_NAME_KEY, description("", BootstrapNodes.getSelectedNode().getName()))
.withRequiredArg(); .withRequiredArg();
parser.accepts(BOOTSTRAP_NODE_IP_KEY, description("", BootstrapNodes.DEFAULT.getIp())) parser.accepts(BOOTSTRAP_NODE_IP_KEY, description("", BootstrapNodes.getSelectedNode().getIp()))
.withRequiredArg(); .withRequiredArg();
parser.accepts(BOOTSTRAP_NODE_PORT_KEY, description("", BootstrapNodes.DEFAULT.getPort())) parser.accepts(BOOTSTRAP_NODE_PORT_KEY, description("", BootstrapNodes.getSelectedNode().getPort()))
.withRequiredArg() .withRequiredArg()
.ofType(int.class); .ofType(int.class);
parser.accepts(NETWORK_INTERFACE_KEY, description("Network interface", null)) parser.accepts(NETWORK_INTERFACE_KEY, description("Network interface", null))

View file

@ -360,10 +360,15 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
bitcoinNetworkLabel.setText(model.bitcoinNetworkAsString); bitcoinNetworkLabel.setText(model.bitcoinNetworkAsString);
model.walletServiceErrorMsg.addListener((ov, oldValue, newValue) -> { model.walletServiceErrorMsg.addListener((ov, oldValue, newValue) -> {
bitcoinNetworkLabel.setId("splash-error-state-msg"); if (newValue != null) {
bitcoinNetworkLabel.textProperty().unbind(); bitcoinNetworkLabel.setId("splash-error-state-msg");
bitcoinNetworkLabel.setText("Not connected"); bitcoinNetworkLabel.setText("Not connected");
openBTCConnectionErrorPopup(newValue); openBTCConnectionErrorPopup(newValue);
}
else {
bitcoinNetworkLabel.setId("footer-bitcoin-network-label");
bitcoinNetworkLabel.setText(model.bitcoinNetworkAsString);
}
}); });
model.blockchainSyncProgress.addListener((ov, oldValue, newValue) -> { model.blockchainSyncProgress.addListener((ov, oldValue, newValue) -> {
@ -412,11 +417,17 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
setBottomAnchor(numPeersLabel, 7d); setBottomAnchor(numPeersLabel, 7d);
numPeersLabel.textProperty().bind(model.numDHTPeers); numPeersLabel.textProperty().bind(model.numDHTPeers);
model.bootstrapErrorMsg.addListener((ov, oldValue, newValue) -> { model.bootstrapErrorMsg.addListener((ov, oldValue, newValue) -> {
bootstrapLabel.setId("splash-error-state-msg"); if (newValue != null) {
bootstrapLabel.textProperty().unbind(); bootstrapLabel.setId("splash-error-state-msg");
bootstrapLabel.setText("Not connected"); bootstrapLabel.textProperty().unbind();
Popups.openErrorPopup("Error", "Connecting to the P2P network failed. \n" + newValue bootstrapLabel.setText("Not connected");
+ "\nPlease check our internet connection."); Popups.openErrorPopup("Error", "Connecting to the P2P network failed. \n" + newValue
+ "\nPlease check your internet connection.");
}
else {
bootstrapLabel.setId("footer-pane");
bootstrapLabel.textProperty().bind(model.bootstrapInfoFooter);
}
}); });
AnchorPane footerContainer = new AnchorPane(separator, blockchainSyncBox, versionLabel, bootstrapLabel, bootstrapIcon, numPeersLabel) {{ AnchorPane footerContainer = new AnchorPane(separator, blockchainSyncBox, versionLabel, bootstrapLabel, bootstrapIcon, numPeersLabel) {{
@ -549,6 +560,6 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
private void openBTCConnectionErrorPopup(String errorMsg) { private void openBTCConnectionErrorPopup(String errorMsg) {
Popups.openErrorPopup("Error", "Connecting to the bitcoin network failed. \n" + errorMsg Popups.openErrorPopup("Error", "Connecting to the bitcoin network failed. \n" + errorMsg
+ "\nPlease check our internet connection."); + "\nPlease check your internet connection.");
} }
} }

View file

@ -36,6 +36,8 @@ import io.bitsquare.trade.offer.OpenOfferManager;
import io.bitsquare.user.User; import io.bitsquare.user.User;
import io.bitsquare.util.Utilities; import io.bitsquare.util.Utilities;
import org.bitcoinj.store.BlockStoreException;
import com.google.inject.Inject; import com.google.inject.Inject;
import java.util.Timer; import java.util.Timer;
@ -65,6 +67,17 @@ class MainViewModel implements ViewModel {
private static final long BLOCKCHAIN_SYNC_TIMEOUT = 30000; private static final long BLOCKCHAIN_SYNC_TIMEOUT = 30000;
private static final long LOST_CONNECTION_TIMEOUT = 10000; private static final long LOST_CONNECTION_TIMEOUT = 10000;
private final User user;
private final KeyRing keyRing;
private final WalletService walletService;
private final ArbitrationRepository arbitrationRepository;
private final ClientNode clientNode;
private final TradeManager tradeManager;
private final OpenOfferManager openOfferManager;
private final UpdateProcess updateProcess;
private final BSFormatter formatter;
private final int networkId;
// BTC network // BTC network
final StringProperty blockchainSyncInfo = new SimpleStringProperty("Initializing"); final StringProperty blockchainSyncInfo = new SimpleStringProperty("Initializing");
final StringProperty blockchainSyncInfoFooter = new SimpleStringProperty("Initializing"); final StringProperty blockchainSyncInfoFooter = new SimpleStringProperty("Initializing");
@ -98,15 +111,6 @@ class MainViewModel implements ViewModel {
final String bitcoinNetworkAsString; final String bitcoinNetworkAsString;
private final User user;
private KeyRing keyRing;
private final WalletService walletService;
private final ArbitrationRepository arbitrationRepository;
private final ClientNode clientNode;
private final TradeManager tradeManager;
private OpenOfferManager openOfferManager;
private final UpdateProcess updateProcess;
private final BSFormatter formatter;
private Timer blockchainSyncTimeoutTimer; private Timer blockchainSyncTimeoutTimer;
private Timer lostConnectionTimeoutTimer; private Timer lostConnectionTimeoutTimer;
@ -126,6 +130,7 @@ class MainViewModel implements ViewModel {
this.formatter = formatter; this.formatter = formatter;
bitcoinNetworkAsString = formatter.formatBitcoinNetwork(bitcoinNetwork); bitcoinNetworkAsString = formatter.formatBitcoinNetwork(bitcoinNetwork);
networkId = bitcoinNetwork.ordinal();
updateProcess.state.addListener((observableValue, oldValue, newValue) -> applyUpdateState(newValue)); updateProcess.state.addListener((observableValue, oldValue, newValue) -> applyUpdateState(newValue));
applyUpdateState(updateProcess.state.get()); applyUpdateState(updateProcess.state.get());
@ -146,7 +151,8 @@ class MainViewModel implements ViewModel {
public void initBackend() { public void initBackend() {
Platform.runLater(updateProcess::init); Platform.runLater(updateProcess::init);
startBlockchainSyncTimeout(); if (walletService.downloadPercentageProperty().get() > -1)
startBlockchainSyncTimeout();
walletService.downloadPercentageProperty().addListener((ov, oldValue, newValue) -> { walletService.downloadPercentageProperty().addListener((ov, oldValue, newValue) -> {
setBitcoinNetworkSyncProgress((double) newValue); setBitcoinNetworkSyncProgress((double) newValue);
@ -155,9 +161,10 @@ class MainViewModel implements ViewModel {
walletService.numPeersProperty().addListener((observable, oldValue, newValue) -> { walletService.numPeersProperty().addListener((observable, oldValue, newValue) -> {
numBTCPeers.set(String.valueOf(newValue) + " peers"); numBTCPeers.set(String.valueOf(newValue) + " peers");
if ((int) newValue < 1) { if ((int) newValue < 1)
walletServiceErrorMsg.set("We lost connection to the last peer."); walletServiceErrorMsg.set("We lost connection to the last peer.");
} else
walletServiceErrorMsg.set(null);
}); });
// Set executor for all P2PServices // Set executor for all P2PServices
@ -178,10 +185,12 @@ class MainViewModel implements ViewModel {
lostConnectionTimeoutTimer.cancel(); lostConnectionTimeoutTimer.cancel();
lostConnectionTimeoutTimer = null; lostConnectionTimeoutTimer = null;
} }
bootstrapErrorMsg.set(null);
} }
}); });
Observable<BootstrappedPeerBuilder.State> bootstrapStateAsObservable = clientNode.bootstrap(keyRing.getDhtSignatureKeyPair()); clientNode.setExecutor(Platform::runLater);
Observable<BootstrappedPeerBuilder.State> bootstrapStateAsObservable = clientNode.bootstrap(networkId, keyRing.getDhtSignatureKeyPair());
bootstrapStateAsObservable.publish(); bootstrapStateAsObservable.publish();
bootstrapStateAsObservable.subscribe( bootstrapStateAsObservable.subscribe(
state -> Platform.runLater(() -> setBootstrapState(state)), state -> Platform.runLater(() -> setBootstrapState(state)),
@ -229,6 +238,7 @@ class MainViewModel implements ViewModel {
private void onAllServicesInitialized() { private void onAllServicesInitialized() {
log.trace("backend completed"); log.trace("backend completed");
setBitcoinNetworkSyncProgress(walletService.downloadPercentageProperty().get());
tradeManager.getPendingTrades().addListener((ListChangeListener<Trade>) change -> updateNumPendingTrades()); tradeManager.getPendingTrades().addListener((ListChangeListener<Trade>) change -> updateNumPendingTrades());
updateNumPendingTrades(); updateNumPendingTrades();
showAppScreen.set(true); showAppScreen.set(true);
@ -321,6 +331,9 @@ class MainViewModel implements ViewModel {
"You must allow outgoing TCP connections to port 18333 for the bitcoin testnet.\n\n" + "You must allow outgoing TCP connections to port 18333 for the bitcoin testnet.\n\n" +
"See https://github.com/bitsquare/bitsquare/wiki for instructions."); "See https://github.com/bitsquare/bitsquare/wiki for instructions.");
} }
else if (error.getCause() instanceof BlockStoreException) {
walletServiceErrorMsg.set("You cannot run 2 instances of the program.");
}
else if (error.getMessage() != null) { else if (error.getMessage() != null) {
walletServiceErrorMsg.set(error.getMessage()); walletServiceErrorMsg.set(error.getMessage());
} }
@ -365,6 +378,8 @@ class MainViewModel implements ViewModel {
private void setBitcoinNetworkSyncProgress(double value) { private void setBitcoinNetworkSyncProgress(double value) {
blockchainSyncProgress.set(value); blockchainSyncProgress.set(value);
if (value >= 1) { if (value >= 1) {
stopBlockchainSyncTimeout();
blockchainSyncInfo.set("Blockchain synchronization complete."); blockchainSyncInfo.set("Blockchain synchronization complete.");
blockchainSyncIconId.set("image-connection-synced"); blockchainSyncIconId.set("image-connection-synced");
} }

View file

@ -45,20 +45,29 @@
</GridPane.margin> </GridPane.margin>
</TextField> </TextField>
<Label text="P2P network connection:" GridPane.rowIndex="1"/> <Label text="Bitcoin network connected peers:" GridPane.rowIndex="1"/>
<TextField fx:id="connectionType" GridPane.rowIndex="1" GridPane.columnIndex="1" <TextField fx:id="connectedPeersBTC" GridPane.rowIndex="1" GridPane.columnIndex="1"
mouseTransparent="true" focusTraversable="false"/> mouseTransparent="true" focusTraversable="false"/>
<Label text="My external visible P2P network address:" GridPane.rowIndex="2"/>
<TextField fx:id="nodeAddress" GridPane.rowIndex="2" GridPane.columnIndex="1" <Label text="P2P network connection:" GridPane.rowIndex="2"/>
<TextField fx:id="connectionType" GridPane.rowIndex="2" GridPane.columnIndex="1"
mouseTransparent="true" focusTraversable="false"/> mouseTransparent="true" focusTraversable="false"/>
<Label text="P2P bootstrap node address:" GridPane.rowIndex="3"> <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">
<GridPane.margin> <GridPane.margin>
<Insets bottom="-15"/> <Insets bottom="-15"/>
</GridPane.margin> </GridPane.margin>
</Label> </Label>
<TextField fx:id="bootstrapNodeAddress" GridPane.rowIndex="3" GridPane.columnIndex="1" <TextField fx:id="bootstrapNodeAddress" GridPane.rowIndex="5" GridPane.columnIndex="1"
mouseTransparent="true" focusTraversable="false"> mouseTransparent="true" focusTraversable="false">
<GridPane.margin> <GridPane.margin>
<Insets bottom="-15"/> <Insets bottom="-15"/>

View file

@ -18,6 +18,7 @@
package io.bitsquare.gui.main.settings.network; package io.bitsquare.gui.main.settings.network;
import io.bitsquare.btc.BitcoinNetwork; import io.bitsquare.btc.BitcoinNetwork;
import io.bitsquare.btc.WalletService;
import io.bitsquare.gui.common.view.FxmlView; import io.bitsquare.gui.common.view.FxmlView;
import io.bitsquare.gui.common.view.InitializableView; import io.bitsquare.gui.common.view.InitializableView;
import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.gui.util.BSFormatter;
@ -28,23 +29,31 @@ import javax.inject.Inject;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.*; import javafx.scene.control.*;
import static javafx.beans.binding.Bindings.createStringBinding;
@FxmlView @FxmlView
public class NetworkSettingsView extends InitializableView { public class NetworkSettingsView extends InitializableView {
private final String bitcoinNetworkString; private final String bitcoinNetworkString;
private final WalletService walletService;
private final ClientNode clientNode; private final ClientNode clientNode;
@FXML TextField bitcoinNetwork, connectionType, nodeAddress, bootstrapNodeAddress; @FXML TextField bitcoinNetwork, connectionType, nodeAddress, bootstrapNodeAddress, connectedPeersBTC, connectedPeersP2P;
@Inject @Inject
public NetworkSettingsView(BitcoinNetwork bitcoinNetwork, ClientNode clientNode, BSFormatter formatter) { public NetworkSettingsView(BitcoinNetwork bitcoinNetwork, WalletService walletService, ClientNode clientNode, BSFormatter formatter) {
this.walletService = walletService;
this.bitcoinNetworkString = formatter.formatBitcoinNetwork(bitcoinNetwork); this.bitcoinNetworkString = formatter.formatBitcoinNetwork(bitcoinNetwork);
this.clientNode = clientNode; this.clientNode = clientNode;
} }
public void initialize() { public void initialize() {
bitcoinNetwork.setText(bitcoinNetworkString); bitcoinNetwork.setText(bitcoinNetworkString);
connectedPeersBTC.textProperty().bind(createStringBinding(() -> String.valueOf(walletService.numPeersProperty().get()), walletService
.numPeersProperty()));
connectionType.setText(clientNode.getConnectionType().toString()); connectionType.setText(clientNode.getConnectionType().toString());
connectedPeersP2P.textProperty().bind(createStringBinding(() -> String.valueOf(clientNode.numPeersProperty().get()), clientNode.numPeersProperty()));
nodeAddress.setText(clientNode.getAddress().toString()); nodeAddress.setText(clientNode.getAddress().toString());
bootstrapNodeAddress.setText(clientNode.getBootstrapNodeAddress().toString()); bootstrapNodeAddress.setText(clientNode.getBootstrapNodeAddress().toString());
} }