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

@ -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;
}
}