mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-24 07:07:43 +01:00
Merge remote-tracking branch 'origin/Development' into Development
This commit is contained in:
commit
25b17e638f
106 changed files with 3045 additions and 1082 deletions
|
@ -39,7 +39,7 @@ public class Version {
|
|||
// VERSION = 0.3.5 -> LOCAL_DB_VERSION = 2
|
||||
// VERSION = 0.4.0 -> LOCAL_DB_VERSION = 3
|
||||
// VERSION = 0.4.2 -> LOCAL_DB_VERSION = 4
|
||||
public static final int LOCAL_DB_VERSION = 4;
|
||||
public static final int LOCAL_DB_VERSION = 4;
|
||||
|
||||
// The version nr. of the current protocol. The offer holds that version.
|
||||
// A taker will check the version of the offers to see if his version is compatible.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package io.bitsquare.common;
|
||||
|
||||
public class OptionKeys {
|
||||
public class CommonOptionKeys {
|
||||
public static final String LOG_LEVEL_KEY = "logLevel";
|
||||
public static final String IGNORE_DEV_MSG_KEY = "ignoreDevMsg";
|
||||
}
|
|
@ -68,6 +68,7 @@ public abstract class Task<T extends Model> {
|
|||
}
|
||||
|
||||
protected void failed() {
|
||||
log.error(errorMessage);
|
||||
taskHandler.handleErrorMessage(errorMessage);
|
||||
}
|
||||
|
||||
|
|
|
@ -80,7 +80,9 @@ public class Utilities {
|
|||
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTimeInSec,
|
||||
TimeUnit.SECONDS, new ArrayBlockingQueue<>(maximumPoolSize), threadFactory);
|
||||
executor.allowCoreThreadTimeOut(true);
|
||||
executor.setRejectedExecutionHandler((r, e) -> log.warn("RejectedExecutionHandler called"));
|
||||
executor.setRejectedExecutionHandler((r, e) -> {
|
||||
log.debug("RejectedExecutionHandler called");
|
||||
});
|
||||
return executor;
|
||||
}
|
||||
|
||||
|
@ -99,7 +101,9 @@ public class Utilities {
|
|||
executor.allowCoreThreadTimeOut(true);
|
||||
executor.setMaximumPoolSize(maximumPoolSize);
|
||||
executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
||||
executor.setRejectedExecutionHandler((r, e) -> log.warn("RejectedExecutionHandler called"));
|
||||
executor.setRejectedExecutionHandler((r, e) -> {
|
||||
log.debug("RejectedExecutionHandler called");
|
||||
});
|
||||
return executor;
|
||||
}
|
||||
|
||||
|
@ -135,7 +139,7 @@ public class Utilities {
|
|||
? "64" : "32";
|
||||
} else if (osArch.contains("arm")) {
|
||||
// armv8 is 64 bit, armv7l is 32 bit
|
||||
return osArch.contains("64") || osArch.contains("v8") ? "64" : "32";
|
||||
return osArch.contains("64") || osArch.contains("v8") ? "64" : "32";
|
||||
} else if (isLinux()) {
|
||||
return osArch.startsWith("i") ? "32" : "64";
|
||||
} else {
|
||||
|
|
|
@ -67,17 +67,9 @@ public class FileManager<T> {
|
|||
saveNowInternal(serializable);
|
||||
return null;
|
||||
};
|
||||
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
FileManager.this.shutDown();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
UserThread.execute(FileManager.this::shutDown);
|
||||
}, "FileManager.ShutDownHook"));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ package io.bitsquare.alert;
|
|||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.name.Named;
|
||||
import io.bitsquare.common.OptionKeys;
|
||||
import io.bitsquare.common.CommonOptionKeys;
|
||||
import io.bitsquare.common.crypto.KeyRing;
|
||||
import io.bitsquare.p2p.P2PService;
|
||||
import io.bitsquare.p2p.storage.HashMapChangedListener;
|
||||
|
@ -56,7 +56,7 @@ public class AlertManager {
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public AlertManager(P2PService p2PService, KeyRing keyRing, User user, @Named(OptionKeys.IGNORE_DEV_MSG_KEY) boolean ignoreDevMsg) {
|
||||
public AlertManager(P2PService p2PService, KeyRing keyRing, User user, @Named(CommonOptionKeys.IGNORE_DEV_MSG_KEY) boolean ignoreDevMsg) {
|
||||
this.p2PService = p2PService;
|
||||
this.keyRing = keyRing;
|
||||
this.user = user;
|
||||
|
|
|
@ -19,7 +19,7 @@ package io.bitsquare.alert;
|
|||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.name.Named;
|
||||
import io.bitsquare.common.OptionKeys;
|
||||
import io.bitsquare.common.CommonOptionKeys;
|
||||
import io.bitsquare.common.crypto.KeyRing;
|
||||
import io.bitsquare.crypto.DecryptedMsgWithPubKey;
|
||||
import io.bitsquare.p2p.Message;
|
||||
|
@ -58,7 +58,7 @@ public class PrivateNotificationManager {
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public PrivateNotificationManager(P2PService p2PService, KeyRing keyRing, @Named(OptionKeys.IGNORE_DEV_MSG_KEY) boolean ignoreDevMsg) {
|
||||
public PrivateNotificationManager(P2PService p2PService, KeyRing keyRing, @Named(CommonOptionKeys.IGNORE_DEV_MSG_KEY) boolean ignoreDevMsg) {
|
||||
this.p2PService = p2PService;
|
||||
this.keyRing = keyRing;
|
||||
|
||||
|
|
|
@ -20,11 +20,13 @@ package io.bitsquare.app;
|
|||
import ch.qos.logback.classic.Level;
|
||||
import io.bitsquare.BitsquareException;
|
||||
import io.bitsquare.btc.BitcoinNetwork;
|
||||
import io.bitsquare.btc.BtcOptionKeys;
|
||||
import io.bitsquare.btc.UserAgent;
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.common.CommonOptionKeys;
|
||||
import io.bitsquare.common.crypto.KeyStorage;
|
||||
import io.bitsquare.common.util.Utilities;
|
||||
import io.bitsquare.network.OptionKeys;
|
||||
import io.bitsquare.network.NetworkOptionKeys;
|
||||
import io.bitsquare.storage.Storage;
|
||||
import io.bitsquare.util.spring.JOptCommandLinePropertySource;
|
||||
import joptsimple.OptionSet;
|
||||
|
@ -56,7 +58,12 @@ public class BitsquareEnvironment extends StandardEnvironment {
|
|||
public static final String DEFAULT_USER_DATA_DIR = defaultUserDataDir();
|
||||
|
||||
public static final String APP_NAME_KEY = "appName";
|
||||
public static final String DEFAULT_APP_NAME = "Bitsquare";
|
||||
|
||||
public static void setDefaultAppName(String defaultAppName) {
|
||||
DEFAULT_APP_NAME = defaultAppName;
|
||||
}
|
||||
|
||||
public static String DEFAULT_APP_NAME = "Bitsquare";
|
||||
|
||||
public static final String APP_DATA_DIR_KEY = "appDataDir";
|
||||
public static final String DEFAULT_APP_DATA_DIR = appDataDir(DEFAULT_USER_DATA_DIR, DEFAULT_APP_NAME);
|
||||
|
@ -77,7 +84,7 @@ public class BitsquareEnvironment extends StandardEnvironment {
|
|||
private final String btcNetworkDir;
|
||||
private final String logLevel;
|
||||
private BitcoinNetwork bitcoinNetwork;
|
||||
private final String seedNodes, ignoreDevMsg;
|
||||
private final String btcSeedNodes, seedNodes, ignoreDevMsg, useTorForBtc, myAddress, banList;
|
||||
|
||||
public BitsquareEnvironment(OptionSet options) {
|
||||
this(new JOptCommandLinePropertySource(BITSQUARE_COMMANDLINE_PROPERTY_SOURCE_NAME, checkNotNull(
|
||||
|
@ -126,17 +133,33 @@ public class BitsquareEnvironment extends StandardEnvironment {
|
|||
(String) commandLineProperties.getProperty(APP_DATA_DIR_KEY) :
|
||||
appDataDir(userDataDir, appName);
|
||||
|
||||
logLevel = commandLineProperties.containsProperty(io.bitsquare.common.OptionKeys.LOG_LEVEL_KEY) ?
|
||||
(String) commandLineProperties.getProperty(io.bitsquare.common.OptionKeys.LOG_LEVEL_KEY) :
|
||||
logLevel = commandLineProperties.containsProperty(CommonOptionKeys.LOG_LEVEL_KEY) ?
|
||||
(String) commandLineProperties.getProperty(CommonOptionKeys.LOG_LEVEL_KEY) :
|
||||
LOG_LEVEL_DEFAULT;
|
||||
|
||||
seedNodes = commandLineProperties.containsProperty(OptionKeys.SEED_NODES_KEY) ?
|
||||
(String) commandLineProperties.getProperty(OptionKeys.SEED_NODES_KEY) :
|
||||
seedNodes = commandLineProperties.containsProperty(NetworkOptionKeys.SEED_NODES_KEY) ?
|
||||
(String) commandLineProperties.getProperty(NetworkOptionKeys.SEED_NODES_KEY) :
|
||||
"";
|
||||
|
||||
ignoreDevMsg = commandLineProperties.containsProperty(io.bitsquare.common.OptionKeys.IGNORE_DEV_MSG_KEY) ?
|
||||
(String) commandLineProperties.getProperty(io.bitsquare.common.OptionKeys.IGNORE_DEV_MSG_KEY) :
|
||||
myAddress = commandLineProperties.containsProperty(NetworkOptionKeys.MY_ADDRESS) ?
|
||||
(String) commandLineProperties.getProperty(NetworkOptionKeys.MY_ADDRESS) :
|
||||
"";
|
||||
banList = commandLineProperties.containsProperty(NetworkOptionKeys.BAN_LIST) ?
|
||||
(String) commandLineProperties.getProperty(NetworkOptionKeys.BAN_LIST) :
|
||||
"";
|
||||
|
||||
ignoreDevMsg = commandLineProperties.containsProperty(CommonOptionKeys.IGNORE_DEV_MSG_KEY) ?
|
||||
(String) commandLineProperties.getProperty(CommonOptionKeys.IGNORE_DEV_MSG_KEY) :
|
||||
"";
|
||||
|
||||
btcSeedNodes = commandLineProperties.containsProperty(BtcOptionKeys.BTC_SEED_NODES) ?
|
||||
(String) commandLineProperties.getProperty(BtcOptionKeys.BTC_SEED_NODES) :
|
||||
"";
|
||||
|
||||
useTorForBtc = commandLineProperties.containsProperty(BtcOptionKeys.USE_TOR_FOR_BTC) ?
|
||||
(String) commandLineProperties.getProperty(BtcOptionKeys.USE_TOR_FOR_BTC) :
|
||||
"";
|
||||
|
||||
|
||||
MutablePropertySources propertySources = this.getPropertySources();
|
||||
propertySources.addFirst(commandLineProperties);
|
||||
|
@ -193,10 +216,15 @@ public class BitsquareEnvironment extends StandardEnvironment {
|
|||
|
||||
{
|
||||
setProperty(APP_DATA_DIR_KEY, appDataDir);
|
||||
setProperty(io.bitsquare.common.OptionKeys.LOG_LEVEL_KEY, logLevel);
|
||||
setProperty(CommonOptionKeys.LOG_LEVEL_KEY, logLevel);
|
||||
|
||||
setProperty(OptionKeys.SEED_NODES_KEY, seedNodes);
|
||||
setProperty(io.bitsquare.common.OptionKeys.IGNORE_DEV_MSG_KEY, ignoreDevMsg);
|
||||
setProperty(NetworkOptionKeys.SEED_NODES_KEY, seedNodes);
|
||||
setProperty(NetworkOptionKeys.MY_ADDRESS, myAddress);
|
||||
setProperty(NetworkOptionKeys.BAN_LIST, banList);
|
||||
setProperty(CommonOptionKeys.IGNORE_DEV_MSG_KEY, ignoreDevMsg);
|
||||
|
||||
setProperty(BtcOptionKeys.BTC_SEED_NODES, btcSeedNodes);
|
||||
setProperty(BtcOptionKeys.USE_TOR_FOR_BTC, useTorForBtc);
|
||||
|
||||
setProperty(APP_NAME_KEY, appName);
|
||||
setProperty(USER_DATA_DIR_KEY, userDataDir);
|
||||
|
@ -208,9 +236,9 @@ public class BitsquareEnvironment extends StandardEnvironment {
|
|||
|
||||
setProperty(Storage.DIR_KEY, Paths.get(btcNetworkDir, "db").toString());
|
||||
setProperty(KeyStorage.DIR_KEY, Paths.get(btcNetworkDir, "keys").toString());
|
||||
setProperty(OptionKeys.TOR_DIR, Paths.get(btcNetworkDir, "tor").toString());
|
||||
setProperty(NetworkOptionKeys.TOR_DIR, Paths.get(btcNetworkDir, "tor").toString());
|
||||
|
||||
setProperty(OptionKeys.NETWORK_ID, String.valueOf(bitcoinNetwork.ordinal()));
|
||||
setProperty(NetworkOptionKeys.NETWORK_ID, String.valueOf(bitcoinNetwork.ordinal()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -17,11 +17,25 @@
|
|||
|
||||
package io.bitsquare.app;
|
||||
|
||||
import io.bitsquare.BitsquareException;
|
||||
import io.bitsquare.btc.BitcoinNetwork;
|
||||
import io.bitsquare.btc.BtcOptionKeys;
|
||||
import io.bitsquare.btc.RegTestHost;
|
||||
import io.bitsquare.common.CommonOptionKeys;
|
||||
import io.bitsquare.network.NetworkOptionKeys;
|
||||
import io.bitsquare.p2p.P2PService;
|
||||
import io.bitsquare.util.joptsimple.EnumValueConverter;
|
||||
import joptsimple.OptionException;
|
||||
import joptsimple.OptionParser;
|
||||
import joptsimple.OptionSet;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static io.bitsquare.app.BitsquareEnvironment.*;
|
||||
import static java.lang.String.format;
|
||||
import static java.lang.String.join;
|
||||
|
||||
|
@ -55,7 +69,53 @@ public abstract class BitsquareExecutable {
|
|||
this.doExecute(options);
|
||||
}
|
||||
|
||||
protected abstract void customizeOptionParsing(OptionParser parser);
|
||||
protected void customizeOptionParsing(OptionParser parser) {
|
||||
parser.accepts(USER_DATA_DIR_KEY, description("User data directory", DEFAULT_USER_DATA_DIR))
|
||||
.withRequiredArg();
|
||||
parser.accepts(APP_NAME_KEY, description("Application name", DEFAULT_APP_NAME))
|
||||
.withRequiredArg();
|
||||
parser.accepts(APP_DATA_DIR_KEY, description("Application data directory", DEFAULT_APP_DATA_DIR))
|
||||
.withRequiredArg();
|
||||
parser.accepts(CommonOptionKeys.LOG_LEVEL_KEY, description("Log level [OFF, ALL, ERROR, WARN, INFO, DEBUG, TRACE]", LOG_LEVEL_DEFAULT))
|
||||
.withRequiredArg();
|
||||
|
||||
parser.accepts(NetworkOptionKeys.SEED_NODES_KEY, description("Override hard coded seed nodes as comma separated list: E.g. rxdkppp3vicnbgqt.onion:8002, mfla72c4igh5ta2t.onion:8002", ""))
|
||||
.withRequiredArg();
|
||||
parser.accepts(NetworkOptionKeys.MY_ADDRESS, description("My own onion address (used for botstrap nodes to exclude itself)", ""))
|
||||
.withRequiredArg();
|
||||
parser.accepts(NetworkOptionKeys.BAN_LIST, description("Nodes to exclude from network connections.", ""))
|
||||
.withRequiredArg();
|
||||
|
||||
parser.accepts(CommonOptionKeys.IGNORE_DEV_MSG_KEY, description("If set to true all signed messages from Bitsquare developers are ignored " +
|
||||
"(Global alert, Version update alert, Filters for offers, nodes or payment account data)", false))
|
||||
.withRequiredArg()
|
||||
.ofType(boolean.class);
|
||||
|
||||
parser.accepts(BtcOptionKeys.BTC_SEED_NODES, description("Custom seed nodes used for BitcoinJ.", ""))
|
||||
.withRequiredArg();
|
||||
parser.accepts(BtcOptionKeys.USE_TOR_FOR_BTC, description("If set to true BitcoinJ is routed over our native Tor instance.", ""))
|
||||
.withRequiredArg();
|
||||
|
||||
// use a fixed port as arbitrator use that for his ID
|
||||
parser.accepts(NetworkOptionKeys.PORT_KEY, description("Port to listen on", 9999))
|
||||
.withRequiredArg()
|
||||
.ofType(int.class);
|
||||
parser.accepts(NetworkOptionKeys.USE_LOCALHOST, description("Use localhost network for development", false))
|
||||
.withRequiredArg()
|
||||
.ofType(boolean.class);
|
||||
parser.accepts(NetworkOptionKeys.MAX_CONNECTIONS, description("Max. connections a peer will try to keep", P2PService.MAX_CONNECTIONS_DEFAULT))
|
||||
.withRequiredArg()
|
||||
.ofType(int.class);
|
||||
parser.accepts(BitcoinNetwork.KEY, description("Bitcoin network", BitcoinNetwork.DEFAULT))
|
||||
.withRequiredArg()
|
||||
.ofType(BitcoinNetwork.class)
|
||||
.withValuesConvertedBy(new EnumValueConverter(BitcoinNetwork.class));
|
||||
|
||||
parser.accepts(RegTestHost.KEY, description("", RegTestHost.DEFAULT))
|
||||
.withRequiredArg()
|
||||
.ofType(RegTestHost.class)
|
||||
.withValuesConvertedBy(new EnumValueConverter(RegTestHost.class));
|
||||
}
|
||||
|
||||
protected static String description(String descText, Object defaultValue) {
|
||||
String description = "";
|
||||
|
@ -67,4 +127,20 @@ public abstract class BitsquareExecutable {
|
|||
}
|
||||
|
||||
protected abstract void doExecute(OptionSet options);
|
||||
|
||||
|
||||
public static void initAppDir(String appDir) {
|
||||
Path dir = Paths.get(appDir);
|
||||
if (Files.exists(dir)) {
|
||||
if (!Files.isWritable(dir))
|
||||
throw new BitsquareException("Application data directory '%s' is not writeable", dir);
|
||||
else
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Files.createDirectory(dir);
|
||||
} catch (IOException ex) {
|
||||
throw new BitsquareException(ex, "Application data directory '%s' could not be created", dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,8 +27,12 @@ import io.bitsquare.btc.TradeWalletService;
|
|||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.btc.exceptions.TransactionVerificationException;
|
||||
import io.bitsquare.btc.exceptions.WalletException;
|
||||
import io.bitsquare.common.Timer;
|
||||
import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.common.crypto.KeyRing;
|
||||
import io.bitsquare.common.crypto.PubKeyRing;
|
||||
import io.bitsquare.common.handlers.ErrorMessageHandler;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
import io.bitsquare.crypto.DecryptedMsgWithPubKey;
|
||||
import io.bitsquare.p2p.BootstrapListener;
|
||||
import io.bitsquare.p2p.Message;
|
||||
|
@ -37,8 +41,10 @@ import io.bitsquare.p2p.P2PService;
|
|||
import io.bitsquare.p2p.messaging.SendMailboxMessageListener;
|
||||
import io.bitsquare.storage.Storage;
|
||||
import io.bitsquare.trade.Contract;
|
||||
import io.bitsquare.trade.Tradable;
|
||||
import io.bitsquare.trade.Trade;
|
||||
import io.bitsquare.trade.TradeManager;
|
||||
import io.bitsquare.trade.closed.ClosedTradableManager;
|
||||
import io.bitsquare.trade.offer.OpenOffer;
|
||||
import io.bitsquare.trade.offer.OpenOfferManager;
|
||||
import javafx.collections.FXCollections;
|
||||
|
@ -51,9 +57,12 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
import javax.inject.Named;
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class DisputeManager {
|
||||
private static final Logger log = LoggerFactory.getLogger(DisputeManager.class);
|
||||
|
@ -61,6 +70,7 @@ public class DisputeManager {
|
|||
private final TradeWalletService tradeWalletService;
|
||||
private final WalletService walletService;
|
||||
private final TradeManager tradeManager;
|
||||
private ClosedTradableManager closedTradableManager;
|
||||
private final OpenOfferManager openOfferManager;
|
||||
private final P2PService p2PService;
|
||||
private final KeyRing keyRing;
|
||||
|
@ -72,6 +82,7 @@ public class DisputeManager {
|
|||
private final CopyOnWriteArraySet<DecryptedMsgWithPubKey> decryptedDirectMessageWithPubKeys = new CopyOnWriteArraySet<>();
|
||||
private final Map<String, Dispute> openDisputes;
|
||||
private final Map<String, Dispute> closedDisputes;
|
||||
private Map<String, Timer> delayMsgMap = new HashMap<>();
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -83,6 +94,7 @@ public class DisputeManager {
|
|||
TradeWalletService tradeWalletService,
|
||||
WalletService walletService,
|
||||
TradeManager tradeManager,
|
||||
ClosedTradableManager closedTradableManager,
|
||||
OpenOfferManager openOfferManager,
|
||||
KeyRing keyRing,
|
||||
@Named(Storage.DIR_KEY) File storageDir) {
|
||||
|
@ -90,6 +102,7 @@ public class DisputeManager {
|
|||
this.tradeWalletService = tradeWalletService;
|
||||
this.walletService = walletService;
|
||||
this.tradeManager = tradeManager;
|
||||
this.closedTradableManager = closedTradableManager;
|
||||
this.openOfferManager = openOfferManager;
|
||||
this.keyRing = keyRing;
|
||||
|
||||
|
@ -192,43 +205,55 @@ public class DisputeManager {
|
|||
log.warn("Unsupported message at dispatchMessage.\nmessage=" + message);
|
||||
}
|
||||
|
||||
public void sendOpenNewDisputeMessage(Dispute dispute) {
|
||||
public void sendOpenNewDisputeMessage(Dispute dispute, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
if (!disputes.contains(dispute)) {
|
||||
DisputeCommunicationMessage disputeCommunicationMessage = new DisputeCommunicationMessage(dispute.getTradeId(),
|
||||
keyRing.getPubKeyRing().hashCode(),
|
||||
true,
|
||||
"System message: " + (dispute.isSupportTicket() ?
|
||||
"You opened a request for support."
|
||||
: "You opened a request for a dispute.\n\n" + disputeInfo),
|
||||
p2PService.getAddress());
|
||||
disputeCommunicationMessage.setIsSystemMessage(true);
|
||||
dispute.addDisputeMessage(disputeCommunicationMessage);
|
||||
disputes.add(dispute);
|
||||
disputesObservableList.add(dispute);
|
||||
final Optional<Dispute> storedDisputeOptional = findDispute(dispute.getTradeId(), dispute.getTraderId());
|
||||
if (!storedDisputeOptional.isPresent()) {
|
||||
DisputeCommunicationMessage disputeCommunicationMessage = new DisputeCommunicationMessage(dispute.getTradeId(),
|
||||
keyRing.getPubKeyRing().hashCode(),
|
||||
true,
|
||||
"System message: " + (dispute.isSupportTicket() ?
|
||||
"You opened a request for support."
|
||||
: "You opened a request for a dispute.\n\n" + disputeInfo),
|
||||
p2PService.getAddress());
|
||||
disputeCommunicationMessage.setIsSystemMessage(true);
|
||||
dispute.addDisputeMessage(disputeCommunicationMessage);
|
||||
disputes.add(dispute);
|
||||
disputesObservableList.add(dispute);
|
||||
|
||||
p2PService.sendEncryptedMailboxMessage(dispute.getContract().arbitratorNodeAddress,
|
||||
dispute.getArbitratorPubKeyRing(),
|
||||
new OpenNewDisputeMessage(dispute, p2PService.getAddress()),
|
||||
new SendMailboxMessageListener() {
|
||||
@Override
|
||||
public void onArrived() {
|
||||
disputeCommunicationMessage.setArrived(true);
|
||||
p2PService.sendEncryptedMailboxMessage(dispute.getContract().arbitratorNodeAddress,
|
||||
dispute.getArbitratorPubKeyRing(),
|
||||
new OpenNewDisputeMessage(dispute, p2PService.getAddress()),
|
||||
new SendMailboxMessageListener() {
|
||||
@Override
|
||||
public void onArrived() {
|
||||
disputeCommunicationMessage.setArrived(true);
|
||||
resultHandler.handleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStoredInMailbox() {
|
||||
disputeCommunicationMessage.setStoredInMailbox(true);
|
||||
resultHandler.handleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFault(String errorMessage) {
|
||||
log.error("sendEncryptedMessage failed");
|
||||
errorMessageHandler.handleErrorMessage("Sending dispute message failed: " + errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStoredInMailbox() {
|
||||
disputeCommunicationMessage.setStoredInMailbox(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFault(String errorMessage) {
|
||||
log.error("sendEncryptedMessage failed");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
);
|
||||
} else {
|
||||
final String msg = "We got a dispute already open for that trade and trading peer.\n" +
|
||||
"TradeId = " + dispute.getTradeId();
|
||||
log.warn(msg);
|
||||
errorMessageHandler.handleErrorMessage(msg);
|
||||
}
|
||||
} else {
|
||||
log.warn("We got a dispute msg what we have already stored. TradeId = " + dispute.getTradeId());
|
||||
final String msg = "We got a dispute msg what we have already stored. TradeId = " + dispute.getTradeId();
|
||||
log.warn(msg);
|
||||
errorMessageHandler.handleErrorMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -256,43 +281,49 @@ public class DisputeManager {
|
|||
disputeFromOpener.getArbitratorPubKeyRing(),
|
||||
disputeFromOpener.isSupportTicket()
|
||||
);
|
||||
DisputeCommunicationMessage disputeCommunicationMessage = new DisputeCommunicationMessage(dispute.getTradeId(),
|
||||
keyRing.getPubKeyRing().hashCode(),
|
||||
true,
|
||||
"System message: " + (dispute.isSupportTicket() ?
|
||||
"Your trading peer has requested support due technical problems. Please wait for further instructions."
|
||||
: "Your trading peer has requested a dispute.\n\n" + disputeInfo),
|
||||
p2PService.getAddress());
|
||||
disputeCommunicationMessage.setIsSystemMessage(true);
|
||||
dispute.addDisputeMessage(disputeCommunicationMessage);
|
||||
disputes.add(dispute);
|
||||
disputesObservableList.add(dispute);
|
||||
final Optional<Dispute> storedDisputeOptional = findDispute(dispute.getTradeId(), dispute.getTraderId());
|
||||
if (!storedDisputeOptional.isPresent()) {
|
||||
DisputeCommunicationMessage disputeCommunicationMessage = new DisputeCommunicationMessage(dispute.getTradeId(),
|
||||
keyRing.getPubKeyRing().hashCode(),
|
||||
true,
|
||||
"System message: " + (dispute.isSupportTicket() ?
|
||||
"Your trading peer has requested support due technical problems. Please wait for further instructions."
|
||||
: "Your trading peer has requested a dispute.\n\n" + disputeInfo),
|
||||
p2PService.getAddress());
|
||||
disputeCommunicationMessage.setIsSystemMessage(true);
|
||||
dispute.addDisputeMessage(disputeCommunicationMessage);
|
||||
disputes.add(dispute);
|
||||
disputesObservableList.add(dispute);
|
||||
|
||||
// we mirrored dispute already!
|
||||
Contract contract = dispute.getContract();
|
||||
PubKeyRing peersPubKeyRing = dispute.isDisputeOpenerIsBuyer() ? contract.getBuyerPubKeyRing() : contract.getSellerPubKeyRing();
|
||||
NodeAddress peerNodeAddress = dispute.isDisputeOpenerIsBuyer() ? contract.getBuyerNodeAddress() : contract.getSellerNodeAddress();
|
||||
log.trace("sendPeerOpenedDisputeMessage to peerAddress " + peerNodeAddress);
|
||||
p2PService.sendEncryptedMailboxMessage(peerNodeAddress,
|
||||
peersPubKeyRing,
|
||||
new PeerOpenedDisputeMessage(dispute, p2PService.getAddress()),
|
||||
new SendMailboxMessageListener() {
|
||||
@Override
|
||||
public void onArrived() {
|
||||
disputeCommunicationMessage.setArrived(true);
|
||||
}
|
||||
// we mirrored dispute already!
|
||||
Contract contract = dispute.getContract();
|
||||
PubKeyRing peersPubKeyRing = dispute.isDisputeOpenerIsBuyer() ? contract.getBuyerPubKeyRing() : contract.getSellerPubKeyRing();
|
||||
NodeAddress peerNodeAddress = dispute.isDisputeOpenerIsBuyer() ? contract.getBuyerNodeAddress() : contract.getSellerNodeAddress();
|
||||
log.trace("sendPeerOpenedDisputeMessage to peerAddress " + peerNodeAddress);
|
||||
p2PService.sendEncryptedMailboxMessage(peerNodeAddress,
|
||||
peersPubKeyRing,
|
||||
new PeerOpenedDisputeMessage(dispute, p2PService.getAddress()),
|
||||
new SendMailboxMessageListener() {
|
||||
@Override
|
||||
public void onArrived() {
|
||||
disputeCommunicationMessage.setArrived(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStoredInMailbox() {
|
||||
disputeCommunicationMessage.setStoredInMailbox(true);
|
||||
}
|
||||
@Override
|
||||
public void onStoredInMailbox() {
|
||||
disputeCommunicationMessage.setStoredInMailbox(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFault(String errorMessage) {
|
||||
log.error("sendEncryptedMessage failed");
|
||||
@Override
|
||||
public void onFault(String errorMessage) {
|
||||
log.error("sendEncryptedMessage failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
} else {
|
||||
log.warn("We got a dispute already open for that trade and trading peer.\n" +
|
||||
"TradeId = " + dispute.getTradeId());
|
||||
}
|
||||
}
|
||||
|
||||
// traders send msg to the arbitrator or arbitrator to 1 trader (trader to trader is not allowed)
|
||||
|
@ -423,10 +454,16 @@ public class DisputeManager {
|
|||
Dispute dispute = openNewDisputeMessage.dispute;
|
||||
if (isArbitrator(dispute)) {
|
||||
if (!disputes.contains(dispute)) {
|
||||
dispute.setStorage(getDisputeStorage());
|
||||
disputes.add(dispute);
|
||||
disputesObservableList.add(dispute);
|
||||
sendPeerOpenedDisputeMessage(dispute);
|
||||
final Optional<Dispute> storedDisputeOptional = findDispute(dispute.getTradeId(), dispute.getTraderId());
|
||||
if (!storedDisputeOptional.isPresent()) {
|
||||
dispute.setStorage(getDisputeStorage());
|
||||
disputes.add(dispute);
|
||||
disputesObservableList.add(dispute);
|
||||
sendPeerOpenedDisputeMessage(dispute);
|
||||
} else {
|
||||
log.warn("We got a dispute already open for that trade and trading peer.\n" +
|
||||
"TradeId = " + dispute.getTradeId());
|
||||
}
|
||||
} else {
|
||||
log.warn("We got a dispute msg what we have already stored. TradeId = " + dispute.getTradeId());
|
||||
}
|
||||
|
@ -439,14 +476,20 @@ public class DisputeManager {
|
|||
private void onPeerOpenedDisputeMessage(PeerOpenedDisputeMessage peerOpenedDisputeMessage) {
|
||||
Dispute dispute = peerOpenedDisputeMessage.dispute;
|
||||
if (!isArbitrator(dispute)) {
|
||||
Optional<Trade> tradeOptional = tradeManager.getTradeById(dispute.getTradeId());
|
||||
if (tradeOptional.isPresent())
|
||||
tradeOptional.get().setDisputeState(Trade.DisputeState.DISPUTE_STARTED_BY_PEER);
|
||||
|
||||
if (!disputes.contains(dispute)) {
|
||||
dispute.setStorage(getDisputeStorage());
|
||||
disputes.add(dispute);
|
||||
disputesObservableList.add(dispute);
|
||||
final Optional<Dispute> storedDisputeOptional = findDispute(dispute.getTradeId(), dispute.getTraderId());
|
||||
if (!storedDisputeOptional.isPresent()) {
|
||||
Optional<Trade> tradeOptional = tradeManager.getTradeById(dispute.getTradeId());
|
||||
if (tradeOptional.isPresent())
|
||||
tradeOptional.get().setDisputeState(Trade.DisputeState.DISPUTE_STARTED_BY_PEER);
|
||||
|
||||
dispute.setStorage(getDisputeStorage());
|
||||
disputes.add(dispute);
|
||||
disputesObservableList.add(dispute);
|
||||
} else {
|
||||
log.warn("We got a dispute already open for that trade and trading peer.\n" +
|
||||
"TradeId = " + dispute.getTradeId());
|
||||
}
|
||||
} else {
|
||||
log.warn("We got a dispute msg what we have already stored. TradeId = " + dispute.getTradeId());
|
||||
}
|
||||
|
@ -457,16 +500,26 @@ public class DisputeManager {
|
|||
|
||||
// a trader can receive a msg from the arbitrator or the arbitrator form a trader. Trader to trader is not allowed.
|
||||
private void onDisputeDirectMessage(DisputeCommunicationMessage disputeCommunicationMessage) {
|
||||
Log.traceCall("disputeDirectMessage " + disputeCommunicationMessage);
|
||||
Optional<Dispute> disputeOptional = findDispute(disputeCommunicationMessage.getTradeId(), disputeCommunicationMessage.getTraderId());
|
||||
Log.traceCall("disputeCommunicationMessage " + disputeCommunicationMessage);
|
||||
final String tradeId = disputeCommunicationMessage.getTradeId();
|
||||
Optional<Dispute> disputeOptional = findDispute(tradeId, disputeCommunicationMessage.getTraderId());
|
||||
final String uid = disputeCommunicationMessage.getUID();
|
||||
if (disputeOptional.isPresent()) {
|
||||
cleanupRetryMap(uid);
|
||||
|
||||
Dispute dispute = disputeOptional.get();
|
||||
if (!dispute.getDisputeCommunicationMessagesAsObservableList().contains(disputeCommunicationMessage))
|
||||
dispute.addDisputeMessage(disputeCommunicationMessage);
|
||||
else
|
||||
log.warn("We got a dispute mail msg what we have already stored. TradeId = " + disputeCommunicationMessage.getTradeId());
|
||||
log.warn("We got a disputeCommunicationMessage what we have already stored. TradeId = " + tradeId);
|
||||
} else {
|
||||
log.warn("We got a dispute mail msg but we don't have a matching dispute. TradeId = " + disputeCommunicationMessage.getTradeId());
|
||||
log.debug("We got a disputeCommunicationMessage but we don't have a matching dispute. TradeId = " + tradeId);
|
||||
if (!delayMsgMap.containsKey(uid)) {
|
||||
Timer timer = UserThread.runAfter(() -> onDisputeDirectMessage(disputeCommunicationMessage), 1);
|
||||
delayMsgMap.put(uid, timer);
|
||||
} else {
|
||||
log.warn("We got a disputeCommunicationMessage after we already repeated to apply the message after a delay. That should never happen. TradeId = " + tradeId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -474,8 +527,12 @@ public class DisputeManager {
|
|||
private void onDisputeResultMessage(DisputeResultMessage disputeResultMessage) {
|
||||
DisputeResult disputeResult = disputeResultMessage.disputeResult;
|
||||
if (!isArbitrator(disputeResult)) {
|
||||
Optional<Dispute> disputeOptional = findDispute(disputeResult.tradeId, disputeResult.traderId);
|
||||
final String tradeId = disputeResult.tradeId;
|
||||
Optional<Dispute> disputeOptional = findDispute(tradeId, disputeResult.traderId);
|
||||
final String uid = disputeResultMessage.getUID();
|
||||
if (disputeOptional.isPresent()) {
|
||||
cleanupRetryMap(uid);
|
||||
|
||||
Dispute dispute = disputeOptional.get();
|
||||
|
||||
DisputeCommunicationMessage disputeCommunicationMessage = disputeResult.getDisputeCommunicationMessage();
|
||||
|
@ -500,65 +557,93 @@ public class DisputeManager {
|
|||
|| (!isBuyer && disputeResult.getWinner() == DisputeResult.Winner.SELLER)
|
||||
|| (isBuyer && disputeResult.getWinner() == DisputeResult.Winner.STALE_MATE)) {
|
||||
|
||||
if (dispute.getDepositTxSerialized() != null) {
|
||||
try {
|
||||
log.debug("do payout Transaction ");
|
||||
|
||||
Transaction signedDisputedPayoutTx = tradeWalletService.traderSignAndFinalizeDisputedPayoutTx(
|
||||
dispute.getDepositTxSerialized(),
|
||||
disputeResult.getArbitratorSignature(),
|
||||
disputeResult.getBuyerPayoutAmount(),
|
||||
disputeResult.getSellerPayoutAmount(),
|
||||
disputeResult.getArbitratorPayoutAmount(),
|
||||
contract.getBuyerPayoutAddressString(),
|
||||
contract.getSellerPayoutAddressString(),
|
||||
disputeResult.getArbitratorAddressAsString(),
|
||||
walletService.getOrCreateAddressEntry(dispute.getTradeId(), AddressEntry.Context.MULTI_SIG),
|
||||
contract.getBuyerBtcPubKey(),
|
||||
contract.getSellerBtcPubKey(),
|
||||
disputeResult.getArbitratorPubKey()
|
||||
);
|
||||
Transaction committedDisputedPayoutTx = tradeWalletService.addTransactionToWallet(signedDisputedPayoutTx);
|
||||
log.debug("broadcast committedDisputedPayoutTx");
|
||||
tradeWalletService.broadcastTx(committedDisputedPayoutTx, new FutureCallback<Transaction>() {
|
||||
@Override
|
||||
public void onSuccess(Transaction transaction) {
|
||||
log.debug("BroadcastTx succeeded. Transaction:" + transaction);
|
||||
final Optional<Trade> tradeOptional = tradeManager.getTradeById(tradeId);
|
||||
Transaction payoutTx = null;
|
||||
if (tradeOptional.isPresent()) {
|
||||
payoutTx = tradeOptional.get().getPayoutTx();
|
||||
} else {
|
||||
final Optional<Tradable> tradableOptional = closedTradableManager.getTradableById(tradeId);
|
||||
if (tradableOptional.isPresent() && tradableOptional.get() instanceof Trade) {
|
||||
payoutTx = ((Trade) tradableOptional.get()).getPayoutTx();
|
||||
}
|
||||
}
|
||||
|
||||
// after successful publish we send peer the tx
|
||||
if (payoutTx == null) {
|
||||
if (dispute.getDepositTxSerialized() != null) {
|
||||
try {
|
||||
log.debug("do payout Transaction ");
|
||||
|
||||
dispute.setDisputePayoutTxId(transaction.getHashAsString());
|
||||
sendPeerPublishedPayoutTxMessage(transaction, dispute, contract);
|
||||
Transaction signedDisputedPayoutTx = tradeWalletService.traderSignAndFinalizeDisputedPayoutTx(
|
||||
dispute.getDepositTxSerialized(),
|
||||
disputeResult.getArbitratorSignature(),
|
||||
disputeResult.getBuyerPayoutAmount(),
|
||||
disputeResult.getSellerPayoutAmount(),
|
||||
disputeResult.getArbitratorPayoutAmount(),
|
||||
contract.getBuyerPayoutAddressString(),
|
||||
contract.getSellerPayoutAddressString(),
|
||||
disputeResult.getArbitratorAddressAsString(),
|
||||
walletService.getOrCreateAddressEntry(dispute.getTradeId(), AddressEntry.Context.MULTI_SIG),
|
||||
contract.getBuyerBtcPubKey(),
|
||||
contract.getSellerBtcPubKey(),
|
||||
disputeResult.getArbitratorPubKey()
|
||||
);
|
||||
Transaction committedDisputedPayoutTx = tradeWalletService.addTransactionToWallet(signedDisputedPayoutTx);
|
||||
log.debug("broadcast committedDisputedPayoutTx");
|
||||
tradeWalletService.broadcastTx(committedDisputedPayoutTx, new FutureCallback<Transaction>() {
|
||||
@Override
|
||||
public void onSuccess(Transaction transaction) {
|
||||
log.debug("BroadcastTx succeeded. Transaction:" + transaction);
|
||||
|
||||
// set state after payout as we call swapTradeEntryToAvailableEntry
|
||||
if (tradeManager.getTradeById(dispute.getTradeId()).isPresent())
|
||||
tradeManager.closeDisputedTrade(dispute.getTradeId());
|
||||
else {
|
||||
Optional<OpenOffer> openOfferOptional = openOfferManager.getOpenOfferById(dispute.getTradeId());
|
||||
if (openOfferOptional.isPresent())
|
||||
openOfferManager.closeOpenOffer(openOfferOptional.get().getOffer());
|
||||
// after successful publish we send peer the tx
|
||||
|
||||
dispute.setDisputePayoutTxId(transaction.getHashAsString());
|
||||
sendPeerPublishedPayoutTxMessage(transaction, dispute, contract);
|
||||
|
||||
// set state after payout as we call swapTradeEntryToAvailableEntry
|
||||
if (tradeManager.getTradeById(dispute.getTradeId()).isPresent())
|
||||
tradeManager.closeDisputedTrade(dispute.getTradeId());
|
||||
else {
|
||||
Optional<OpenOffer> openOfferOptional = openOfferManager.getOpenOfferById(dispute.getTradeId());
|
||||
if (openOfferOptional.isPresent())
|
||||
openOfferManager.closeOpenOffer(openOfferOptional.get().getOffer());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NotNull Throwable t) {
|
||||
// TODO error handling
|
||||
log.error(t.getMessage());
|
||||
}
|
||||
});
|
||||
} catch (AddressFormatException | WalletException | TransactionVerificationException e) {
|
||||
e.printStackTrace();
|
||||
log.error("Error at traderSignAndFinalizeDisputedPayoutTx " + e.getMessage());
|
||||
@Override
|
||||
public void onFailure(@NotNull Throwable t) {
|
||||
log.error(t.getMessage());
|
||||
}
|
||||
});
|
||||
} catch (AddressFormatException | WalletException | TransactionVerificationException e) {
|
||||
e.printStackTrace();
|
||||
log.error("Error at traderSignAndFinalizeDisputedPayoutTx " + e.getMessage());
|
||||
}
|
||||
} else {
|
||||
log.warn("DepositTx is null. TradeId = " + tradeId);
|
||||
}
|
||||
} else {
|
||||
log.warn("DepositTx is null. TradeId = " + disputeResult.tradeId);
|
||||
log.warn("We got already a payout tx. That might be the case if the other peer did not get the " +
|
||||
"payout tx and opened a dispute. TradeId = " + tradeId);
|
||||
dispute.setDisputePayoutTxId(payoutTx.getHashAsString());
|
||||
sendPeerPublishedPayoutTxMessage(payoutTx, dispute, contract);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.warn("We got a dispute msg what we have already stored. TradeId = " + disputeResult.tradeId);
|
||||
log.warn("We got a dispute msg what we have already stored. TradeId = " + tradeId);
|
||||
}
|
||||
} else {
|
||||
log.warn("We got a dispute result msg but we don't have a matching dispute. TradeId = " + disputeResult.tradeId);
|
||||
log.debug("We got a dispute result msg but we don't have a matching dispute. " +
|
||||
"That might happen when we get the disputeResultMessage before the dispute was created. " +
|
||||
"We try again after 1 sec. to apply the disputeResultMessage. TradeId = " + tradeId);
|
||||
if (!delayMsgMap.containsKey(uid)) {
|
||||
// We delay2 sec. to be sure the comm. msg gets added first
|
||||
Timer timer = UserThread.runAfter(() -> onDisputeResultMessage(disputeResultMessage), 2);
|
||||
delayMsgMap.put(uid, timer);
|
||||
} else {
|
||||
log.warn("We got a dispute result msg after we already repeated to apply the message after a delay. " +
|
||||
"That should never happen. TradeId = " + tradeId);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.error("Arbitrator received disputeResultMessage. That must never happen.");
|
||||
|
@ -567,9 +652,26 @@ public class DisputeManager {
|
|||
|
||||
// losing trader or in case of 50/50 the seller gets the tx sent from the winner or buyer
|
||||
private void onDisputedPayoutTxMessage(PeerPublishedPayoutTxMessage peerPublishedPayoutTxMessage) {
|
||||
Transaction transaction = tradeWalletService.addTransactionToWallet(peerPublishedPayoutTxMessage.transaction);
|
||||
findOwnDispute(peerPublishedPayoutTxMessage.tradeId).ifPresent(dispute -> dispute.setDisputePayoutTxId(transaction.getHashAsString()));
|
||||
tradeManager.closeDisputedTrade(peerPublishedPayoutTxMessage.tradeId);
|
||||
final String uid = peerPublishedPayoutTxMessage.getUID();
|
||||
final String tradeId = peerPublishedPayoutTxMessage.tradeId;
|
||||
Optional<Dispute> disputeOptional = findOwnDispute(tradeId);
|
||||
if (disputeOptional.isPresent()) {
|
||||
cleanupRetryMap(uid);
|
||||
|
||||
Transaction transaction = tradeWalletService.addTransactionToWallet(peerPublishedPayoutTxMessage.transaction);
|
||||
disputeOptional.get().setDisputePayoutTxId(transaction.getHashAsString());
|
||||
tradeManager.closeDisputedTrade(tradeId);
|
||||
} else {
|
||||
log.debug("We got a peerPublishedPayoutTxMessage but we don't have a matching dispute. TradeId = " + tradeId);
|
||||
if (!delayMsgMap.containsKey(uid)) {
|
||||
// We delay 3 sec. to be sure the close msg gets added first
|
||||
Timer timer = UserThread.runAfter(() -> onDisputedPayoutTxMessage(peerPublishedPayoutTxMessage), 3);
|
||||
delayMsgMap.put(uid, timer);
|
||||
} else {
|
||||
log.warn("We got a peerPublishedPayoutTxMessage after we already repeated to apply the message after a delay. " +
|
||||
"That should never happen. TradeId = " + tradeId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -607,11 +709,19 @@ public class DisputeManager {
|
|||
}
|
||||
|
||||
public Optional<Dispute> findOwnDispute(String tradeId) {
|
||||
return disputes.stream().filter(e -> e.getTradeId().equals(tradeId)).findAny();
|
||||
return getDisputeStream(tradeId).findAny();
|
||||
}
|
||||
|
||||
public List<Dispute> findDisputesByTradeId(String tradeId) {
|
||||
return disputes.stream().filter(e -> e.getTradeId().equals(tradeId)).collect(Collectors.toList());
|
||||
private Stream<Dispute> getDisputeStream(String tradeId) {
|
||||
return disputes.stream().filter(e -> e.getTradeId().equals(tradeId));
|
||||
}
|
||||
|
||||
private void cleanupRetryMap(String uid) {
|
||||
if (delayMsgMap.containsKey(uid)) {
|
||||
Timer timer = delayMsgMap.remove(uid);
|
||||
if (timer != null)
|
||||
timer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -48,6 +48,9 @@ public class BitcoinModule extends AppModule {
|
|||
File walletDir = new File(env.getRequiredProperty(WalletService.DIR_KEY));
|
||||
bind(File.class).annotatedWith(named(WalletService.DIR_KEY)).toInstance(walletDir);
|
||||
|
||||
bindConstant().annotatedWith(named(BtcOptionKeys.BTC_SEED_NODES)).to(env.getRequiredProperty(BtcOptionKeys.BTC_SEED_NODES));
|
||||
bindConstant().annotatedWith(named(BtcOptionKeys.USE_TOR_FOR_BTC)).to(env.getRequiredProperty(BtcOptionKeys.USE_TOR_FOR_BTC));
|
||||
|
||||
bind(AddressEntryList.class).in(Singleton.class);
|
||||
bind(TradeWalletService.class).in(Singleton.class);
|
||||
bind(WalletService.class).in(Singleton.class);
|
||||
|
|
6
core/src/main/java/io/bitsquare/btc/BtcOptionKeys.java
Normal file
6
core/src/main/java/io/bitsquare/btc/BtcOptionKeys.java
Normal file
|
@ -0,0 +1,6 @@
|
|||
package io.bitsquare.btc;
|
||||
|
||||
public class BtcOptionKeys {
|
||||
public static final String BTC_SEED_NODES = "btcSeedNodes";
|
||||
public static final String USE_TOR_FOR_BTC = "useTorForBtc";
|
||||
}
|
94
core/src/main/java/io/bitsquare/btc/ProxySocketFactory.java
Normal file
94
core/src/main/java/io/bitsquare/btc/ProxySocketFactory.java
Normal file
|
@ -0,0 +1,94 @@
|
|||
|
||||
/**
|
||||
* Copyright (C) 2010-2014 Leon Blakey <lord.quackstar at gmail.com>
|
||||
*
|
||||
* This file is part of PircBotX.
|
||||
*
|
||||
* PircBotX is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* PircBotX is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* PircBotX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package io.bitsquare.btc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import javax.net.SocketFactory;
|
||||
|
||||
/**
|
||||
* A basic SocketFactory for creating sockets that connect through the specified
|
||||
* proxy.
|
||||
*
|
||||
* @author Leon Blakey
|
||||
*/
|
||||
public class ProxySocketFactory extends SocketFactory {
|
||||
protected final Proxy proxy;
|
||||
|
||||
/**
|
||||
* Create all sockets with the specified proxy.
|
||||
*
|
||||
* @param proxy An existing proxy
|
||||
*/
|
||||
public ProxySocketFactory(Proxy proxy) {
|
||||
this.proxy = proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience constructor for creating a proxy with the specified host
|
||||
* and port.
|
||||
*
|
||||
* @param proxyType The type of proxy were connecting to
|
||||
* @param hostname The hostname of the proxy server
|
||||
* @param port The port of the proxy server
|
||||
*/
|
||||
public ProxySocketFactory(Proxy.Type proxyType, String hostname, int port) {
|
||||
this.proxy = new Proxy(proxyType, new InetSocketAddress(hostname, port));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket() throws IOException {
|
||||
Socket socket = new Socket(proxy);
|
||||
return socket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String string, int i) throws IOException, UnknownHostException {
|
||||
Socket socket = new Socket(proxy);
|
||||
socket.connect(new InetSocketAddress(string, i));
|
||||
return socket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String string, int i, InetAddress localAddress, int localPort) throws IOException, UnknownHostException {
|
||||
Socket socket = new Socket(proxy);
|
||||
socket.bind(new InetSocketAddress(localAddress, localPort));
|
||||
socket.connect(new InetSocketAddress(string, i));
|
||||
return socket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress ia, int i) throws IOException {
|
||||
Socket socket = new Socket(proxy);
|
||||
socket.connect(new InetSocketAddress(ia, i));
|
||||
return socket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress ia, int i, InetAddress localAddress, int localPort) throws IOException {
|
||||
Socket socket = new Socket(proxy);
|
||||
socket.bind(new InetSocketAddress(localAddress, localPort));
|
||||
socket.connect(new InetSocketAddress(ia, i));
|
||||
return socket;
|
||||
}
|
||||
}
|
178
core/src/main/java/io/bitsquare/btc/SeedPeersSocks5Dns.java
Normal file
178
core/src/main/java/io/bitsquare/btc/SeedPeersSocks5Dns.java
Normal file
|
@ -0,0 +1,178 @@
|
|||
/**
|
||||
* Copyright 2011 Micheal Swiggs
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.bitsquare.btc;
|
||||
|
||||
import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;
|
||||
import com.runjva.sourceforge.jsocks.protocol.SocksSocket;
|
||||
|
||||
import org.bitcoinj.net.*;
|
||||
import org.bitcoinj.net.discovery.PeerDiscovery;
|
||||
import org.bitcoinj.net.discovery.PeerDiscoveryException;
|
||||
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import javax.annotation.Nullable;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
/**
|
||||
* SeedPeersSocks5Dns resolves peers via Proxy (Socks5) remote DNS.
|
||||
*/
|
||||
public class SeedPeersSocks5Dns implements PeerDiscovery {
|
||||
private Socks5Proxy proxy;
|
||||
private NetworkParameters params;
|
||||
private InetSocketAddress[] seedAddrs;
|
||||
private InetSocketAddress[] seedAddrsIP;
|
||||
private int pnseedIndex;
|
||||
|
||||
private InetSocketAddress[] seedAddrsResolved;
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SeedPeersSocks5Dns.class);
|
||||
|
||||
/**
|
||||
* Supports finding peers by hostname over a socks5 proxy.
|
||||
*
|
||||
* @param Socks5Proxy proxy the socks5 proxy to connect over.
|
||||
* @param NetworkParameters param to be used for seed and port information.
|
||||
*/
|
||||
public SeedPeersSocks5Dns(Socks5Proxy proxy, NetworkParameters params) {
|
||||
|
||||
this.proxy = proxy;
|
||||
this.params = params;
|
||||
this.seedAddrs = convertAddrsString( params.getDnsSeeds(), params.getPort() );
|
||||
|
||||
if( false ) {
|
||||
// This is an example of how .onion servers could be used. Unfortunately there is presently no way
|
||||
// to hand the onion address (or a connected socket) back to bitcoinj without it crashing in PeerAddress.
|
||||
// note: the onion addresses should be added into bitcoinj NetworkParameters classes, eg for mainnet, testnet
|
||||
// not here!
|
||||
this.seedAddrs = new InetSocketAddress[] { InetSocketAddress.createUnresolved( "cajrifqkvalh2ooa.onion", 8333 ),
|
||||
InetSocketAddress.createUnresolved( "bk7yp6epnmcllq72.onion", 8333 )
|
||||
};
|
||||
}
|
||||
|
||||
seedAddrsResolved = new InetSocketAddress[seedAddrs.length];
|
||||
for(int idx = seedAddrs.length; idx < seedAddrsResolved.length; idx ++) {
|
||||
seedAddrsResolved[idx] = seedAddrsIP[idx-seedAddrs.length];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts as an iterator, returning the address of each node in the list sequentially.
|
||||
* Once all the list has been iterated, null will be returned for each subsequent query.
|
||||
*
|
||||
* @return InetSocketAddress - The address/port of the next node.
|
||||
* @throws PeerDiscoveryException
|
||||
*/
|
||||
@Nullable
|
||||
public InetSocketAddress getPeer() throws PeerDiscoveryException {
|
||||
try {
|
||||
return nextPeer();
|
||||
} catch (UnknownHostException e) {
|
||||
throw new PeerDiscoveryException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* worker for getPeer()
|
||||
*/
|
||||
@Nullable
|
||||
private InetSocketAddress nextPeer() throws UnknownHostException, PeerDiscoveryException {
|
||||
if (seedAddrs == null || seedAddrs.length == 0) {
|
||||
throw new PeerDiscoveryException("No IP address seeds configured; unable to find any peers");
|
||||
}
|
||||
|
||||
if (pnseedIndex >= seedAddrsResolved.length) {
|
||||
return null;
|
||||
}
|
||||
if( seedAddrsResolved[pnseedIndex] == null ) {
|
||||
seedAddrsResolved[pnseedIndex] = lookup( proxy, seedAddrs[pnseedIndex] );
|
||||
}
|
||||
log.error("SeedPeersSocks5Dns::nextPeer: " + seedAddrsResolved[pnseedIndex] );
|
||||
|
||||
return seedAddrsResolved[pnseedIndex++];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing all the Bitcoin nodes within the list.
|
||||
*/
|
||||
@Override
|
||||
public InetSocketAddress[] getPeers(long timeoutValue, TimeUnit timeoutUnit) throws PeerDiscoveryException {
|
||||
try {
|
||||
return allPeers();
|
||||
} catch (UnknownHostException e) {
|
||||
throw new PeerDiscoveryException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns all seed peers, performs hostname lookups if necessary.
|
||||
*/
|
||||
private InetSocketAddress[] allPeers() throws UnknownHostException {
|
||||
for (int i = 0; i < seedAddrsResolved.length; ++i) {
|
||||
if( seedAddrsResolved[i] == null ) {
|
||||
seedAddrsResolved[i] = lookup( proxy, seedAddrs[i] );
|
||||
}
|
||||
}
|
||||
return seedAddrsResolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a hostname via remote DNS over socks5 proxy.
|
||||
*/
|
||||
public static InetSocketAddress lookup( Socks5Proxy proxy, InetSocketAddress addr ) {
|
||||
if( !addr.isUnresolved() ) {
|
||||
return addr;
|
||||
}
|
||||
try {
|
||||
SocksSocket proxySocket = new SocksSocket( proxy, addr.getHostString(), addr.getPort() );
|
||||
InetAddress addrResolved = proxySocket.getInetAddress();
|
||||
proxySocket.close();
|
||||
if( addrResolved != null ) {
|
||||
log.info("Resolved " + addr.getHostString() + " to " + addrResolved.getHostAddress() );
|
||||
return new InetSocketAddress(addrResolved, addr.getPort() );
|
||||
}
|
||||
else {
|
||||
// note: .onion nodes fall in here when proxy is Tor. But they have no IP address.
|
||||
// Unfortunately bitcoinj crashes in PeerAddress if it finds an unresolved address.
|
||||
log.error("Connected to " + addr.getHostString() + ". But did not resolve to address." );
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Error resolving " + addr.getHostString() + ". Exception:\n" + e.toString() );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an array of hostnames to array of unresolved InetSocketAddress
|
||||
*/
|
||||
private InetSocketAddress[] convertAddrsString(String[] addrs, int port) {
|
||||
InetSocketAddress[] list = new InetSocketAddress[addrs.length];
|
||||
for( int i = 0; i < addrs.length; i++) {
|
||||
list[i] = InetSocketAddress.createUnresolved(addrs[i], port);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
}
|
||||
}
|
|
@ -519,15 +519,15 @@ public class TradeWalletService {
|
|||
/**
|
||||
* Seller signs payout transaction, buyer has not signed yet.
|
||||
*
|
||||
* @param depositTx Deposit transaction
|
||||
* @param buyerPayoutAmount Payout amount for buyer
|
||||
* @param sellerPayoutAmount Payout amount for seller
|
||||
* @param depositTx Deposit transaction
|
||||
* @param buyerPayoutAmount Payout amount for buyer
|
||||
* @param sellerPayoutAmount Payout amount for seller
|
||||
* @param buyerPayoutAddressString Address for buyer
|
||||
* @param sellerPayoutAddressEntry AddressEntry for seller
|
||||
* @param lockTime Lock time
|
||||
* @param buyerPubKey The public key of the buyer.
|
||||
* @param sellerPubKey The public key of the seller.
|
||||
* @param arbitratorPubKey The public key of the arbitrator.
|
||||
* @param lockTime Lock time
|
||||
* @param buyerPubKey The public key of the buyer.
|
||||
* @param sellerPubKey The public key of the seller.
|
||||
* @param arbitratorPubKey The public key of the arbitrator.
|
||||
* @return DER encoded canonical signature
|
||||
* @throws AddressFormatException
|
||||
* @throws TransactionVerificationException
|
||||
|
@ -567,7 +567,10 @@ public class TradeWalletService {
|
|||
// MS output from prev. tx is index 0
|
||||
Sha256Hash sigHash = preparedPayoutTx.hashForSignature(0, redeemScript, Transaction.SigHash.ALL, false);
|
||||
DeterministicKey keyPair = multiSigAddressEntry.getKeyPair();
|
||||
checkNotNull(keyPair);
|
||||
checkNotNull(keyPair, "multiSigAddressEntry.getKeyPair() must not be null");
|
||||
if (keyPair.isEncrypted())
|
||||
checkNotNull(aesKey);
|
||||
|
||||
ECKey.ECDSASignature sellerSignature = keyPair.sign(sigHash, aesKey).toCanonicalised();
|
||||
|
||||
verifyTransaction(preparedPayoutTx);
|
||||
|
@ -580,16 +583,16 @@ public class TradeWalletService {
|
|||
/**
|
||||
* Buyer creates and signs payout transaction and adds signature of seller to complete the transaction
|
||||
*
|
||||
* @param depositTx Deposit transaction
|
||||
* @param sellerSignature DER encoded canonical signature of seller
|
||||
* @param buyerPayoutAmount Payout amount for buyer
|
||||
* @param sellerPayoutAmount Payout amount for seller
|
||||
* @param buyerPayoutAddressEntry AddressEntry for buyer
|
||||
* @param sellerAddressString Address for seller
|
||||
* @param lockTime Lock time
|
||||
* @param buyerPubKey The public key of the buyer.
|
||||
* @param sellerPubKey The public key of the seller.
|
||||
* @param arbitratorPubKey The public key of the arbitrator.
|
||||
* @param depositTx Deposit transaction
|
||||
* @param sellerSignature DER encoded canonical signature of seller
|
||||
* @param buyerPayoutAmount Payout amount for buyer
|
||||
* @param sellerPayoutAmount Payout amount for seller
|
||||
* @param buyerPayoutAddressEntry AddressEntry for buyer
|
||||
* @param sellerAddressString Address for seller
|
||||
* @param lockTime Lock time
|
||||
* @param buyerPubKey The public key of the buyer.
|
||||
* @param sellerPubKey The public key of the seller.
|
||||
* @param arbitratorPubKey The public key of the arbitrator.
|
||||
* @return The payout transaction
|
||||
* @throws AddressFormatException
|
||||
* @throws TransactionVerificationException
|
||||
|
@ -631,8 +634,12 @@ public class TradeWalletService {
|
|||
Script redeemScript = getMultiSigRedeemScript(buyerPubKey, sellerPubKey, arbitratorPubKey);
|
||||
// MS output from prev. tx is index 0
|
||||
Sha256Hash sigHash = payoutTx.hashForSignature(0, redeemScript, Transaction.SigHash.ALL, false);
|
||||
checkNotNull(multiSigAddressEntry.getKeyPair(), "multiSigAddressEntry.getKeyPair() must not be null");
|
||||
ECKey.ECDSASignature buyerSignature = multiSigAddressEntry.getKeyPair().sign(sigHash, aesKey).toCanonicalised();
|
||||
final DeterministicKey keyPair = multiSigAddressEntry.getKeyPair();
|
||||
checkNotNull(keyPair, "multiSigAddressEntry.getKeyPair() must not be null");
|
||||
if (keyPair.isEncrypted())
|
||||
checkNotNull(aesKey);
|
||||
|
||||
ECKey.ECDSASignature buyerSignature = keyPair.sign(sigHash, aesKey).toCanonicalised();
|
||||
|
||||
TransactionSignature sellerTxSig = new TransactionSignature(ECKey.ECDSASignature.decodeFromDER(sellerSignature), Transaction.SigHash.ALL, false);
|
||||
TransactionSignature buyerTxSig = new TransactionSignature(buyerSignature, Transaction.SigHash.ALL, false);
|
||||
|
@ -643,7 +650,7 @@ public class TradeWalletService {
|
|||
input.setScriptSig(inputScript);
|
||||
|
||||
printTxWithInputs("payoutTx", payoutTx);
|
||||
|
||||
|
||||
verifyTransaction(payoutTx);
|
||||
checkWalletConsistency();
|
||||
checkScriptSig(payoutTx, input, 0);
|
||||
|
@ -714,10 +721,12 @@ public class TradeWalletService {
|
|||
// take care of sorting!
|
||||
Script redeemScript = getMultiSigRedeemScript(buyerPubKey, sellerPubKey, arbitratorPubKey);
|
||||
Sha256Hash sigHash = preparedPayoutTx.hashForSignature(0, redeemScript, Transaction.SigHash.ALL, false);
|
||||
if (arbitratorAddressEntry.getKeyPair() == null)
|
||||
throw new RuntimeException("Unexpected null value: arbitratorAddressEntry.getKeyPair() must not be null");
|
||||
final DeterministicKey keyPair = arbitratorAddressEntry.getKeyPair();
|
||||
checkNotNull(keyPair, "arbitratorAddressEntry.getKeyPair() must not be null");
|
||||
if (keyPair.isEncrypted())
|
||||
checkNotNull(aesKey);
|
||||
|
||||
ECKey.ECDSASignature arbitratorSignature = arbitratorAddressEntry.getKeyPair().sign(sigHash, aesKey).toCanonicalised();
|
||||
ECKey.ECDSASignature arbitratorSignature = keyPair.sign(sigHash, aesKey).toCanonicalised();
|
||||
|
||||
verifyTransaction(preparedPayoutTx);
|
||||
|
||||
|
@ -729,18 +738,18 @@ public class TradeWalletService {
|
|||
/**
|
||||
* A trader who got the signed tx from the arbitrator finalizes the payout tx
|
||||
*
|
||||
* @param depositTxSerialized Serialized deposit tx
|
||||
* @param arbitratorSignature DER encoded canonical signature of arbitrator
|
||||
* @param buyerPayoutAmount Payout amount of the buyer
|
||||
* @param sellerPayoutAmount Payout amount of the seller
|
||||
* @param arbitratorPayoutAmount Payout amount for arbitrator
|
||||
* @param buyerAddressString The address of the buyer.
|
||||
* @param sellerAddressString The address of the seller.
|
||||
* @param arbitratorAddressString The address of the arbitrator.
|
||||
* @param tradersMultiSigAddressEntry The addressEntry of the trader who calls that method
|
||||
* @param buyerPubKey The public key of the buyer.
|
||||
* @param sellerPubKey The public key of the seller.
|
||||
* @param arbitratorPubKey The public key of the arbitrator.
|
||||
* @param depositTxSerialized Serialized deposit tx
|
||||
* @param arbitratorSignature DER encoded canonical signature of arbitrator
|
||||
* @param buyerPayoutAmount Payout amount of the buyer
|
||||
* @param sellerPayoutAmount Payout amount of the seller
|
||||
* @param arbitratorPayoutAmount Payout amount for arbitrator
|
||||
* @param buyerAddressString The address of the buyer.
|
||||
* @param sellerAddressString The address of the seller.
|
||||
* @param arbitratorAddressString The address of the arbitrator.
|
||||
* @param tradersMultiSigAddressEntry The addressEntry of the trader who calls that method
|
||||
* @param buyerPubKey The public key of the buyer.
|
||||
* @param sellerPubKey The public key of the seller.
|
||||
* @param arbitratorPubKey The public key of the arbitrator.
|
||||
* @return The completed payout tx
|
||||
* @throws AddressFormatException
|
||||
* @throws TransactionVerificationException
|
||||
|
@ -791,7 +800,9 @@ public class TradeWalletService {
|
|||
Script redeemScript = getMultiSigRedeemScript(buyerPubKey, sellerPubKey, arbitratorPubKey);
|
||||
Sha256Hash sigHash = payoutTx.hashForSignature(0, redeemScript, Transaction.SigHash.ALL, false);
|
||||
DeterministicKey keyPair = tradersMultiSigAddressEntry.getKeyPair();
|
||||
checkNotNull(keyPair);
|
||||
checkNotNull(keyPair, "tradersMultiSigAddressEntry.getKeyPair() must not be null");
|
||||
if (keyPair.isEncrypted())
|
||||
checkNotNull(aesKey);
|
||||
ECKey.ECDSASignature tradersSignature = keyPair.sign(sigHash, aesKey).toCanonicalised();
|
||||
|
||||
TransactionSignature tradersTxSig = new TransactionSignature(tradersSignature, Transaction.SigHash.ALL, false);
|
||||
|
@ -994,6 +1005,8 @@ public class TradeWalletService {
|
|||
checkNotNull(wallet);
|
||||
ECKey sigKey = input.getOutpoint().getConnectedKey(wallet);
|
||||
checkNotNull(sigKey, "signInput: sigKey must not be null. input.getOutpoint()=" + input.getOutpoint().toString());
|
||||
if (sigKey.isEncrypted())
|
||||
checkNotNull(aesKey);
|
||||
Sha256Hash hash = transaction.hashForSignature(inputIndex, scriptPubKey, Transaction.SigHash.ALL, false);
|
||||
ECKey.ECDSASignature signature = sigKey.sign(hash, aesKey);
|
||||
TransactionSignature txSig = new TransactionSignature(signature, Transaction.SigHash.ALL, false);
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.btc;
|
||||
|
||||
import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;
|
||||
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.kits.WalletAppKit;
|
||||
import org.bitcoinj.net.BlockingClientManager;
|
||||
import org.bitcoinj.core.PeerGroup;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.Proxy;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
public class WalletAppKitBitSquare extends WalletAppKit {
|
||||
private Socks5Proxy socks5Proxy;
|
||||
|
||||
/**
|
||||
* Creates a new WalletAppKit, with a newly created {@link Context}. Files will be stored in the given directory.
|
||||
*/
|
||||
public WalletAppKitBitSquare(NetworkParameters params, Socks5Proxy socks5Proxy, File directory, String filePrefix) {
|
||||
super(params, directory, filePrefix);
|
||||
this.socks5Proxy = socks5Proxy;
|
||||
}
|
||||
|
||||
public Socks5Proxy getProxy() {
|
||||
return socks5Proxy;
|
||||
}
|
||||
|
||||
protected PeerGroup createPeerGroup() throws TimeoutException {
|
||||
|
||||
// no proxy case.
|
||||
if(socks5Proxy == null) {
|
||||
return super.createPeerGroup();
|
||||
}
|
||||
|
||||
// proxy case.
|
||||
Proxy proxy = new Proxy ( Proxy.Type.SOCKS,
|
||||
new InetSocketAddress(socks5Proxy.getInetAddress().getHostName(),
|
||||
socks5Proxy.getPort() ) );
|
||||
|
||||
int CONNECT_TIMEOUT_MSEC = 60 * 1000; // same value used in bitcoinj.
|
||||
ProxySocketFactory proxySocketFactory = new ProxySocketFactory(proxy);
|
||||
BlockingClientManager mgr = new BlockingClientManager(proxySocketFactory);
|
||||
PeerGroup peerGroup = new PeerGroup(params, vChain, mgr);
|
||||
|
||||
mgr.setConnectTimeoutMillis(CONNECT_TIMEOUT_MSEC);
|
||||
peerGroup.setConnectTimeoutMillis(CONNECT_TIMEOUT_MSEC);
|
||||
|
||||
// This enables remote DNS lookup of peers over socks5 proxy.
|
||||
// It is slower, but more private.
|
||||
// This could be turned into a user option.
|
||||
this.setDiscovery( new SeedPeersSocks5Dns(socks5Proxy, params) );
|
||||
|
||||
return peerGroup;
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableList;
|
|||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.Service;
|
||||
import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;
|
||||
import io.bitsquare.btc.listeners.AddressConfidenceListener;
|
||||
import io.bitsquare.btc.listeners.BalanceListener;
|
||||
import io.bitsquare.btc.listeners.TxConfidenceListener;
|
||||
|
@ -30,11 +31,14 @@ import io.bitsquare.common.UserThread;
|
|||
import io.bitsquare.common.handlers.ErrorMessageHandler;
|
||||
import io.bitsquare.common.handlers.ExceptionHandler;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
import io.bitsquare.p2p.NodeAddress;
|
||||
import io.bitsquare.storage.FileUtil;
|
||||
import io.bitsquare.storage.Storage;
|
||||
import io.bitsquare.user.Preferences;
|
||||
import javafx.beans.property.*;
|
||||
import org.bitcoinj.core.*;
|
||||
import org.bitcoinj.crypto.DeterministicKey;
|
||||
import org.bitcoinj.crypto.KeyCrypterScrypt;
|
||||
import org.bitcoinj.kits.WalletAppKit;
|
||||
import org.bitcoinj.params.MainNetParams;
|
||||
import org.bitcoinj.params.RegTestParams;
|
||||
|
@ -52,6 +56,7 @@ import javax.inject.Named;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
|
@ -83,12 +88,13 @@ public class WalletService {
|
|||
private final RegTestHost regTestHost;
|
||||
private final TradeWalletService tradeWalletService;
|
||||
private final AddressEntryList addressEntryList;
|
||||
private final String seedNodes;
|
||||
private final NetworkParameters params;
|
||||
private final File walletDir;
|
||||
private final UserAgent userAgent;
|
||||
private final boolean useTor;
|
||||
|
||||
private WalletAppKit walletAppKit;
|
||||
private WalletAppKitBitSquare walletAppKit;
|
||||
private Wallet wallet;
|
||||
private final IntegerProperty numPeers = new SimpleIntegerProperty(0);
|
||||
private final ObjectProperty<List<Peer>> connectedPeers = new SimpleObjectProperty<>();
|
||||
|
@ -103,14 +109,30 @@ public class WalletService {
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public WalletService(RegTestHost regTestHost, TradeWalletService tradeWalletService, AddressEntryList addressEntryList, UserAgent userAgent,
|
||||
@Named(DIR_KEY) File appDir, Preferences preferences) {
|
||||
public WalletService(RegTestHost regTestHost,
|
||||
TradeWalletService tradeWalletService,
|
||||
AddressEntryList addressEntryList,
|
||||
UserAgent userAgent,
|
||||
@Named(DIR_KEY) File appDir,
|
||||
Preferences preferences,
|
||||
@Named(BtcOptionKeys.BTC_SEED_NODES) String seedNodes,
|
||||
@Named(BtcOptionKeys.USE_TOR_FOR_BTC) String useTorFlagFromOptions) {
|
||||
this.regTestHost = regTestHost;
|
||||
this.tradeWalletService = tradeWalletService;
|
||||
this.addressEntryList = addressEntryList;
|
||||
this.seedNodes = seedNodes;
|
||||
this.params = preferences.getBitcoinNetwork().getParameters();
|
||||
this.walletDir = new File(appDir, "bitcoin");
|
||||
this.userAgent = userAgent;
|
||||
|
||||
// We support a checkbox in the settings to set the use tor flag.
|
||||
// If we get the options set we override that setting.
|
||||
if (useTorFlagFromOptions != null && !useTorFlagFromOptions.isEmpty()) {
|
||||
if (useTorFlagFromOptions.equals("false"))
|
||||
preferences.setUseTorForBitcoinJ(false);
|
||||
else if (useTorFlagFromOptions.equals("true"))
|
||||
preferences.setUseTorForBitcoinJ(true);
|
||||
}
|
||||
useTor = preferences.getUseTorForBitcoinJ();
|
||||
|
||||
storage = new Storage<>(walletDir);
|
||||
|
@ -128,7 +150,7 @@ public class WalletService {
|
|||
// Public Methods
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void initialize(@Nullable DeterministicSeed seed, ResultHandler resultHandler, ExceptionHandler exceptionHandler) {
|
||||
public void initialize(@Nullable DeterministicSeed seed, Socks5Proxy proxy, ResultHandler resultHandler, ExceptionHandler exceptionHandler) {
|
||||
// Tell bitcoinj to execute event handlers on the JavaFX UI thread. This keeps things simple and means
|
||||
// we cannot forget to switch threads when adding event handlers. Unfortunately, the DownloadListener
|
||||
// we give to the app kit is currently an exception and runs on a library thread. It'll get fixed in
|
||||
|
@ -144,7 +166,7 @@ public class WalletService {
|
|||
backupWallet();
|
||||
|
||||
// If seed is non-null it means we are restoring from backup.
|
||||
walletAppKit = new WalletAppKit(params, walletDir, "Bitsquare") {
|
||||
walletAppKit = new WalletAppKitBitSquare(params, proxy, walletDir, "Bitsquare") {
|
||||
@Override
|
||||
protected void onSetupCompleted() {
|
||||
// Don't make the user wait for confirmations for now, as the intention is they're sending it
|
||||
|
@ -245,11 +267,52 @@ public class WalletService {
|
|||
// 1333 / (2800 + 1333) = 0.32 -> 32 % probability that a pub key is in our wallet
|
||||
walletAppKit.setBloomFilterFalsePositiveRate(0.00005);
|
||||
|
||||
log.debug( "seedNodes: " + seedNodes.toString() );
|
||||
|
||||
// Pass custom seed nodes if set in options
|
||||
if (seedNodes != null && !seedNodes.isEmpty()) {
|
||||
|
||||
// todo: this parsing should be more robust,
|
||||
// give validation error if needed.
|
||||
// also: it seems the peer nodes can be overridden in the case
|
||||
// of regtest mode below. is that wanted?
|
||||
String[] nodes = seedNodes.split(",");
|
||||
List<PeerAddress> peerAddressList = new ArrayList<PeerAddress>();
|
||||
for(String node : nodes) {
|
||||
String[] parts = node.split(":");
|
||||
if( parts.length == 2 ) {
|
||||
// note: this will cause a DNS request if hostname used.
|
||||
// note: DNS requests are routed over socks5 proxy, if used.
|
||||
// fixme: .onion hostnames will fail! see comments in SeedPeersSocks5Dns
|
||||
InetSocketAddress addr;
|
||||
if( proxy != null ) {
|
||||
InetSocketAddress unresolved = InetSocketAddress.createUnresolved(parts[0], Integer.parseInt(parts[1]));
|
||||
// proxy remote DNS request happens here.
|
||||
addr = SeedPeersSocks5Dns.lookup( proxy, unresolved );
|
||||
}
|
||||
else {
|
||||
// DNS request happens here. if it fails, addr.isUnresolved() == true.
|
||||
addr = new InetSocketAddress( parts[0], Integer.parseInt(parts[1]) );
|
||||
}
|
||||
// note: isUnresolved check should be removed once we fix PeerAddress
|
||||
if( addr != null && !addr.isUnresolved() ) {
|
||||
peerAddressList.add( new PeerAddress( addr.getAddress(), addr.getPort() ));
|
||||
}
|
||||
}
|
||||
}
|
||||
if(peerAddressList.size() > 0) {
|
||||
PeerAddress peerAddressListFixed[] = new PeerAddress[peerAddressList.size()];
|
||||
log.debug( "seedNodes parsed: " + peerAddressListFixed.toString() );
|
||||
|
||||
walletAppKit.setPeerNodes(peerAddressList.toArray(peerAddressListFixed));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Get bitcoinj running over our tor proxy. BlockingClientManager need to be used to use the socket
|
||||
// from jtorproxy. To get supported it via nio / netty will be harder
|
||||
if (useTor && params.getId().equals(NetworkParameters.ID_MAINNET))
|
||||
walletAppKit.useTor();
|
||||
// We do not call walletAppKit.useTor() anymore because that would turn
|
||||
// on orchid Tor, which we do not want. Instead, we create a Tor proxy
|
||||
// later.
|
||||
// if (useTor && params.getId().equals(NetworkParameters.ID_MAINNET))
|
||||
// walletAppKit.useTor();
|
||||
|
||||
// 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.
|
||||
|
@ -317,7 +380,7 @@ public class WalletService {
|
|||
Context.propagate(ctx);
|
||||
walletAppKit.stopAsync();
|
||||
walletAppKit.awaitTerminated();
|
||||
initialize(seed, resultHandler, exceptionHandler);
|
||||
initialize(seed, walletAppKit.getProxy(), resultHandler, exceptionHandler);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
log.error("Executing task failed. " + t.getMessage());
|
||||
|
@ -342,6 +405,36 @@ public class WalletService {
|
|||
this.aesKey = aesKey;
|
||||
}
|
||||
|
||||
public void decryptWallet(@NotNull KeyParameter key) {
|
||||
wallet.decrypt(key);
|
||||
addressEntryList.stream().forEach(e -> {
|
||||
|
||||
final DeterministicKey keyPair = e.getKeyPair();
|
||||
if (keyPair != null && keyPair.isEncrypted())
|
||||
e.setDeterministicKey(keyPair.decrypt(key));
|
||||
});
|
||||
|
||||
setAesKey(null);
|
||||
addressEntryList.queueUpForSave();
|
||||
}
|
||||
|
||||
public void encryptWallet(KeyCrypterScrypt keyCrypterScrypt, KeyParameter key) {
|
||||
if (this.aesKey != null) {
|
||||
log.warn("encryptWallet called but we have a aesKey already set. " +
|
||||
"We decryptWallet with the old key before we apply the new key.");
|
||||
decryptWallet(this.aesKey);
|
||||
}
|
||||
|
||||
wallet.encrypt(keyCrypterScrypt, key);
|
||||
addressEntryList.stream().forEach(e -> {
|
||||
final DeterministicKey keyPair = e.getKeyPair();
|
||||
if (keyPair != null && keyPair.isEncrypted())
|
||||
e.setDeterministicKey(keyPair.encrypt(keyCrypterScrypt, key));
|
||||
});
|
||||
setAesKey(key);
|
||||
addressEntryList.queueUpForSave();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Listener
|
||||
|
@ -736,7 +829,7 @@ public class WalletService {
|
|||
}
|
||||
return fee;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Withdrawal Fee calculation
|
||||
|
|
|
@ -19,7 +19,7 @@ package io.bitsquare.filter;
|
|||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.name.Named;
|
||||
import io.bitsquare.common.OptionKeys;
|
||||
import io.bitsquare.common.CommonOptionKeys;
|
||||
import io.bitsquare.common.crypto.KeyRing;
|
||||
import io.bitsquare.common.util.Tuple3;
|
||||
import io.bitsquare.common.util.Utilities;
|
||||
|
@ -59,7 +59,7 @@ public class FilterManager {
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public FilterManager(P2PService p2PService, KeyRing keyRing, User user, @Named(OptionKeys.IGNORE_DEV_MSG_KEY) boolean ignoreDevMsg) {
|
||||
public FilterManager(P2PService p2PService, KeyRing keyRing, User user, @Named(CommonOptionKeys.IGNORE_DEV_MSG_KEY) boolean ignoreDevMsg) {
|
||||
this.p2PService = p2PService;
|
||||
this.keyRing = keyRing;
|
||||
this.user = user;
|
||||
|
|
|
@ -19,7 +19,7 @@ package io.bitsquare.filter;
|
|||
|
||||
import com.google.inject.Singleton;
|
||||
import io.bitsquare.app.AppModule;
|
||||
import io.bitsquare.common.OptionKeys;
|
||||
import io.bitsquare.common.CommonOptionKeys;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
@ -36,6 +36,6 @@ public class FilterModule extends AppModule {
|
|||
@Override
|
||||
protected final void configure() {
|
||||
bind(FilterManager.class).in(Singleton.class);
|
||||
bindConstant().annotatedWith(named(OptionKeys.IGNORE_DEV_MSG_KEY)).to(env.getRequiredProperty(OptionKeys.IGNORE_DEV_MSG_KEY));
|
||||
bindConstant().annotatedWith(named(CommonOptionKeys.IGNORE_DEV_MSG_KEY)).to(env.getRequiredProperty(CommonOptionKeys.IGNORE_DEV_MSG_KEY));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,6 +75,7 @@ public class CurrencyUtil {
|
|||
result.add(new CryptoCurrency("XMR", "Monero"));
|
||||
result.add(new CryptoCurrency("SC", "Siacoin"));
|
||||
result.add(new CryptoCurrency("ETH", "Ether"));
|
||||
result.add(new CryptoCurrency("ETHC", "EtherClassic"));
|
||||
result.add(new CryptoCurrency("LTC", "Litecoin"));
|
||||
result.add(new CryptoCurrency("DASH", "Dash"));
|
||||
result.add(new CryptoCurrency("NMC", "Namecoin"));
|
||||
|
@ -94,6 +95,7 @@ public class CurrencyUtil {
|
|||
result.add(new CryptoCurrency("XCP", "Counterparty"));
|
||||
result.add(new CryptoCurrency("XRP", "Ripple"));
|
||||
result.add(new CryptoCurrency("FAIR", "FairCoin"));
|
||||
result.add(new CryptoCurrency("FLO", "FlorinCoin"));
|
||||
result.add(new CryptoCurrency("MKR", "Maker", true));
|
||||
result.add(new CryptoCurrency("DGD", "DigixDAO Tokens", true));
|
||||
result.add(new CryptoCurrency("DAO", "DAO", true));
|
||||
|
@ -128,14 +130,16 @@ public class CurrencyUtil {
|
|||
result.add(new CryptoCurrency("USDT", "USD Tether"));
|
||||
result.add(new CryptoCurrency("EURT", "EUR Tether"));
|
||||
result.add(new CryptoCurrency("JPYT", "JPY Tether"));
|
||||
result.add(new CryptoCurrency("WDC", "Worldcoin"));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<CryptoCurrency> getMainCryptoCurrencies() {
|
||||
final List<CryptoCurrency> result = new ArrayList<>();
|
||||
result.add(new CryptoCurrency("XMR", "Monero"));
|
||||
result.add(new CryptoCurrency("SC", "Siacoin"));
|
||||
result.add(new CryptoCurrency("ETH", "Ether"));
|
||||
result.add(new CryptoCurrency("ETHC", "EtherClassic"));
|
||||
result.add(new CryptoCurrency("LTC", "Litecoin"));
|
||||
result.add(new CryptoCurrency("DASH", "Dash"));
|
||||
result.add(new CryptoCurrency("NMC", "Namecoin"));
|
||||
|
|
|
@ -60,7 +60,7 @@ public final class SpecificBanksAccountContractData extends BankAccountContractD
|
|||
|
||||
@Override
|
||||
public String getPaymentDetailsForTradePopup() {
|
||||
return getPaymentDetailsForTradePopup() + "\n" +
|
||||
"Accepted banks: " + Joiner.on(", ").join(acceptedBanks) + "\n";
|
||||
return super.getPaymentDetailsForTradePopup() + "\n" +
|
||||
"Accepted banks: " + Joiner.on(", ").join(acceptedBanks);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,15 +57,6 @@ public abstract class BuyerTrade extends Trade {
|
|||
((BuyerProtocol) tradeProtocol).onFiatPaymentStarted(resultHandler, errorMessageHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reSendConfirmation() {
|
||||
if (state == Trade.State.BUYER_SENT_FIAT_PAYMENT_INITIATED_MSG) {
|
||||
log.info("reSendConfirmation onFiatPaymentStarted");
|
||||
onFiatPaymentStarted(() -> log.debug("onFiatPaymentStarted succeeded"),
|
||||
log::warn);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Coin getPayoutAmount() {
|
||||
checkNotNull(getTradeAmount(), "Invalid state: getTradeAmount() = null");
|
||||
|
|
|
@ -56,15 +56,6 @@ public abstract class SellerTrade extends Trade {
|
|||
((SellerProtocol) tradeProtocol).onFiatPaymentReceived(resultHandler, errorMessageHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reSendConfirmation() {
|
||||
if (state == State.SELLER_SENT_FIAT_PAYMENT_RECEIPT_MSG) {
|
||||
log.info("reSendConfirmation onFiatPaymentReceived");
|
||||
onFiatPaymentReceived(() -> log.debug("onFiatPaymentReceived succeeded"),
|
||||
log::warn);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Coin getPayoutAmount() {
|
||||
return FeePolicy.getSecurityDeposit();
|
||||
|
|
|
@ -52,6 +52,8 @@ import javax.annotation.Nullable;
|
|||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Holds all data which are relevant to the trade, but not those which are only needed in the trade process as shared data between tasks. Those data are
|
||||
|
@ -170,6 +172,7 @@ public abstract class Trade implements Tradable, Model {
|
|||
transient private StringProperty errorMessageProperty;
|
||||
transient private ObjectProperty<Coin> tradeAmountProperty;
|
||||
transient private ObjectProperty<Fiat> tradeVolumeProperty;
|
||||
transient private Set<DecryptedMsgWithPubKey> mailboxMessageSet = new HashSet<>();
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -210,6 +213,7 @@ public abstract class Trade implements Tradable, Model {
|
|||
initStateProperties();
|
||||
initAmountProperty();
|
||||
errorMessageProperty = new SimpleStringProperty(errorMessage);
|
||||
mailboxMessageSet = new HashSet<>();
|
||||
} catch (Throwable t) {
|
||||
log.warn("Cannot be deserialized." + t.getMessage());
|
||||
}
|
||||
|
@ -242,16 +246,13 @@ public abstract class Trade implements Tradable, Model {
|
|||
|
||||
createProtocol();
|
||||
|
||||
log.trace("decryptedMsgWithPubKey = " + decryptedMsgWithPubKey);
|
||||
if (decryptedMsgWithPubKey != null) {
|
||||
log.trace("init: decryptedMsgWithPubKey = " + decryptedMsgWithPubKey);
|
||||
if (decryptedMsgWithPubKey != null && !mailboxMessageSet.contains(decryptedMsgWithPubKey)) {
|
||||
mailboxMessageSet.add(decryptedMsgWithPubKey);
|
||||
tradeProtocol.applyMailboxMessage(decryptedMsgWithPubKey, this);
|
||||
}
|
||||
|
||||
reSendConfirmation();
|
||||
}
|
||||
|
||||
public abstract void reSendConfirmation();
|
||||
|
||||
protected void initStateProperties() {
|
||||
stateProperty = new SimpleObjectProperty<>(state);
|
||||
disputeStateProperty = new SimpleObjectProperty<>(disputeState);
|
||||
|
@ -292,8 +293,13 @@ public abstract class Trade implements Tradable, Model {
|
|||
}
|
||||
|
||||
public void setMailboxMessage(DecryptedMsgWithPubKey decryptedMsgWithPubKey) {
|
||||
log.trace("setMailboxMessage " + decryptedMsgWithPubKey);
|
||||
log.trace("setMailboxMessage decryptedMsgWithPubKey=" + decryptedMsgWithPubKey);
|
||||
this.decryptedMsgWithPubKey = decryptedMsgWithPubKey;
|
||||
|
||||
if (tradeProtocol != null && decryptedMsgWithPubKey != null && !mailboxMessageSet.contains(decryptedMsgWithPubKey)) {
|
||||
mailboxMessageSet.add(decryptedMsgWithPubKey);
|
||||
tradeProtocol.applyMailboxMessage(decryptedMsgWithPubKey, this);
|
||||
}
|
||||
}
|
||||
|
||||
public DecryptedMsgWithPubKey getMailboxMessage() {
|
||||
|
@ -313,7 +319,6 @@ public abstract class Trade implements Tradable, Model {
|
|||
this.state = state;
|
||||
stateProperty.set(state);
|
||||
persist();
|
||||
persist();
|
||||
}
|
||||
|
||||
public void setDisputeState(DisputeState disputeState) {
|
||||
|
|
|
@ -26,6 +26,7 @@ import io.bitsquare.btc.TradeWalletService;
|
|||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.btc.pricefeed.PriceFeed;
|
||||
import io.bitsquare.common.crypto.KeyRing;
|
||||
import io.bitsquare.common.handlers.ErrorMessageHandler;
|
||||
import io.bitsquare.common.handlers.FaultHandler;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
import io.bitsquare.crypto.DecryptedMsgWithPubKey;
|
||||
|
@ -158,6 +159,7 @@ public class TradeManager {
|
|||
}
|
||||
|
||||
public void onAllServicesInitialized() {
|
||||
Log.traceCall();
|
||||
if (p2PService.isBootstrapped())
|
||||
initPendingTrades();
|
||||
else
|
||||
|
@ -262,8 +264,9 @@ public class TradeManager {
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void checkOfferAvailability(Offer offer,
|
||||
ResultHandler resultHandler) {
|
||||
offer.checkOfferAvailability(getOfferAvailabilityModel(offer), resultHandler);
|
||||
ResultHandler resultHandler,
|
||||
ErrorMessageHandler errorMessageHandler) {
|
||||
offer.checkOfferAvailability(getOfferAvailabilityModel(offer), resultHandler, errorMessageHandler);
|
||||
}
|
||||
|
||||
// When closing take offer view, we are not interested in the onCheckOfferAvailability result anymore, so remove from the map
|
||||
|
@ -278,13 +281,15 @@ public class TradeManager {
|
|||
Offer offer,
|
||||
String paymentAccountId,
|
||||
boolean useSavingsWallet,
|
||||
TradeResultHandler tradeResultHandler) {
|
||||
TradeResultHandler tradeResultHandler,
|
||||
ErrorMessageHandler errorMessageHandler) {
|
||||
final OfferAvailabilityModel model = getOfferAvailabilityModel(offer);
|
||||
offer.checkOfferAvailability(model,
|
||||
() -> {
|
||||
if (offer.getState() == Offer.State.AVAILABLE)
|
||||
createTrade(amount, tradePrice, fundsNeededForTrade, offer, paymentAccountId, useSavingsWallet, model, tradeResultHandler);
|
||||
});
|
||||
},
|
||||
errorMessage -> errorMessageHandler.handleErrorMessage(errorMessage));
|
||||
}
|
||||
|
||||
private void createTrade(Coin amount,
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package io.bitsquare.trade.exceptions;
|
||||
|
||||
public class MarketPriceNotAvailableException extends Exception {
|
||||
public MarketPriceNotAvailableException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -24,12 +24,14 @@ import io.bitsquare.btc.pricefeed.MarketPrice;
|
|||
import io.bitsquare.btc.pricefeed.PriceFeed;
|
||||
import io.bitsquare.common.crypto.KeyRing;
|
||||
import io.bitsquare.common.crypto.PubKeyRing;
|
||||
import io.bitsquare.common.handlers.ErrorMessageHandler;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
import io.bitsquare.common.util.JsonExclude;
|
||||
import io.bitsquare.p2p.NodeAddress;
|
||||
import io.bitsquare.p2p.storage.payload.RequiresOwnerIsOnlinePayload;
|
||||
import io.bitsquare.p2p.storage.payload.StoragePayload;
|
||||
import io.bitsquare.payment.PaymentMethod;
|
||||
import io.bitsquare.trade.exceptions.MarketPriceNotAvailableException;
|
||||
import io.bitsquare.trade.exceptions.TradePriceOutOfToleranceException;
|
||||
import io.bitsquare.trade.protocol.availability.OfferAvailabilityModel;
|
||||
import io.bitsquare.trade.protocol.availability.OfferAvailabilityProtocol;
|
||||
|
@ -272,7 +274,8 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload
|
|||
// Availability
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void checkOfferAvailability(OfferAvailabilityModel model, ResultHandler resultHandler) {
|
||||
public void checkOfferAvailability(OfferAvailabilityModel model, ResultHandler resultHandler,
|
||||
ErrorMessageHandler errorMessageHandler) {
|
||||
availabilityProtocol = new OfferAvailabilityProtocol(model,
|
||||
() -> {
|
||||
cancelAvailabilityRequest();
|
||||
|
@ -282,6 +285,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload
|
|||
if (availabilityProtocol != null)
|
||||
availabilityProtocol.cancel();
|
||||
log.error(errorMessage);
|
||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||
});
|
||||
availabilityProtocol.sendOfferAvailabilityRequest();
|
||||
}
|
||||
|
@ -382,11 +386,14 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload
|
|||
}
|
||||
}
|
||||
|
||||
public void checkTradePriceTolerance(long takersTradePrice) throws TradePriceOutOfToleranceException, IllegalArgumentException {
|
||||
public void checkTradePriceTolerance(long takersTradePrice) throws TradePriceOutOfToleranceException, MarketPriceNotAvailableException, IllegalArgumentException {
|
||||
checkArgument(takersTradePrice > 0, "takersTradePrice must be positive");
|
||||
Fiat tradePriceAsFiat = Fiat.valueOf(getCurrencyCode(), takersTradePrice);
|
||||
Fiat offerPriceAsFiat = getPrice();
|
||||
checkArgument(offerPriceAsFiat != null, "offerPriceAsFiat must not be null");
|
||||
|
||||
if (offerPriceAsFiat == null)
|
||||
throw new MarketPriceNotAvailableException("Market price required for calculating trade price is not available.");
|
||||
|
||||
double factor = (double) takersTradePrice / (double) offerPriceAsFiat.value;
|
||||
// We allow max. 2 % difference between own offer price calculation and takers calculation.
|
||||
// Market price might be different at offerers and takers side so we need a bit of tolerance.
|
||||
|
@ -545,7 +552,8 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload
|
|||
"\n\tid='" + id + '\'' +
|
||||
"\n\tdirection=" + direction +
|
||||
"\n\tcurrencyCode='" + currencyCode + '\'' +
|
||||
"\n\tdate=" + date +
|
||||
"\n\tdate=" + new Date(date) +
|
||||
"\n\tdateAsTime=" + date +
|
||||
"\n\tfiatPrice=" + fiatPrice +
|
||||
"\n\tmarketPriceMargin=" + marketPriceMargin +
|
||||
"\n\tuseMarketBasedPrice=" + useMarketBasedPrice +
|
||||
|
|
|
@ -100,13 +100,13 @@ public class OfferBookService {
|
|||
}
|
||||
}
|
||||
|
||||
public void refreshOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
public void refreshTTL(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
boolean result = p2PService.refreshTTL(offer, true);
|
||||
if (result) {
|
||||
log.trace("Add offer to network was successful. Offer ID = " + offer.getId());
|
||||
log.trace("Refresh TTL was successful. Offer ID = " + offer.getId());
|
||||
resultHandler.handleResult();
|
||||
} else {
|
||||
errorMessageHandler.handleErrorMessage("Add offer failed");
|
||||
errorMessageHandler.handleErrorMessage("Refresh TTL failed.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,4 +137,7 @@ public class OfferBookService {
|
|||
removeOffer(offer, null, null);
|
||||
}
|
||||
|
||||
public boolean isBootstrapped() {
|
||||
return p2PService.isBootstrapped();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ import io.bitsquare.p2p.peers.PeerManager;
|
|||
import io.bitsquare.storage.Storage;
|
||||
import io.bitsquare.trade.TradableList;
|
||||
import io.bitsquare.trade.closed.ClosedTradableManager;
|
||||
import io.bitsquare.trade.exceptions.MarketPriceNotAvailableException;
|
||||
import io.bitsquare.trade.exceptions.TradePriceOutOfToleranceException;
|
||||
import io.bitsquare.trade.handlers.TransactionResultHandler;
|
||||
import io.bitsquare.trade.protocol.availability.AvailabilityResult;
|
||||
|
@ -118,8 +119,9 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
openOffers.forEach(e -> e.getOffer().setPriceFeed(priceFeed));
|
||||
|
||||
// In case the app did get killed the shutDown from the modules is not called, so we use a shutdown hook
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(OpenOfferManager.this::shutDown,
|
||||
"OpenOfferManager.ShutDownHook"));
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
UserThread.execute(OpenOfferManager.this::shutDown);
|
||||
}, "OpenOfferManager.ShutDownHook"));
|
||||
}
|
||||
|
||||
public void onAllServicesInitialized() {
|
||||
|
@ -153,22 +155,26 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
log.info("remove all open offers at shutDown");
|
||||
// we remove own offers from offerbook when we go offline
|
||||
// Normally we use a delay for broadcasting to the peers, but at shut down we want to get it fast out
|
||||
closeAllOpenOffers(completeHandler);
|
||||
}
|
||||
|
||||
public void closeAllOpenOffers(@Nullable Runnable completeHandler) {
|
||||
openOffers.forEach(openOffer -> offerBookService.removeOfferAtShutDown(openOffer.getOffer()));
|
||||
if (completeHandler != null)
|
||||
UserThread.runAfter(completeHandler::run, openOffers.size() * 100 + 200, TimeUnit.MILLISECONDS);
|
||||
final int size = openOffers.size();
|
||||
if (offerBookService.isBootstrapped()) {
|
||||
openOffers.forEach(openOffer -> offerBookService.removeOfferAtShutDown(openOffer.getOffer()));
|
||||
if (completeHandler != null)
|
||||
UserThread.runAfter(completeHandler::run, size * 200 + 500, TimeUnit.MILLISECONDS);
|
||||
} else {
|
||||
if (completeHandler != null)
|
||||
completeHandler.run();
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAllOpenOffers(@Nullable Runnable completeHandler) {
|
||||
final int size = openOffers.size();
|
||||
List<OpenOffer> openOffersList = new ArrayList<>(openOffers);
|
||||
openOffersList.forEach(openOffer -> removeOpenOffer(openOffer, () -> {
|
||||
}, errorMessage -> {
|
||||
}));
|
||||
if (completeHandler != null)
|
||||
UserThread.runAfter(completeHandler::run, openOffers.size() * 100 + 200, TimeUnit.MILLISECONDS);
|
||||
UserThread.runAfter(completeHandler::run, size * 200 + 500, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -357,7 +363,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
AvailabilityResult availabilityResult;
|
||||
if (openOfferOptional.isPresent() && openOfferOptional.get().getState() == OpenOffer.State.AVAILABLE) {
|
||||
final Offer offer = openOfferOptional.get().getOffer();
|
||||
if (!preferences.getIgnoreTradersList().stream().filter(i -> i.equals(offer.getOffererNodeAddress().hostName)).findAny().isPresent()) {
|
||||
if (!preferences.getIgnoreTradersList().stream().filter(i -> i.equals(offer.getOffererNodeAddress().getHostNameWithoutPostFix())).findAny().isPresent()) {
|
||||
availabilityResult = AvailabilityResult.AVAILABLE;
|
||||
List<NodeAddress> acceptedArbitrators = user.getAcceptedArbitratorAddresses();
|
||||
if (acceptedArbitrators != null && !acceptedArbitrators.isEmpty()) {
|
||||
|
@ -371,6 +377,9 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
} catch (TradePriceOutOfToleranceException e) {
|
||||
log.warn("Trade price check failed because takers price is outside out tolerance.");
|
||||
availabilityResult = AvailabilityResult.PRICE_OUT_OF_TOLERANCE;
|
||||
} catch (MarketPriceNotAvailableException e) {
|
||||
log.warn(e.getMessage());
|
||||
availabilityResult = AvailabilityResult.MARKET_PRICE_NOT_AVAILABLE;
|
||||
} catch (Throwable e) {
|
||||
log.warn("Trade price check failed. " + e.getMessage());
|
||||
availabilityResult = AvailabilityResult.UNKNOWN_FAILURE;
|
||||
|
@ -494,10 +503,10 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
final ArrayList<OpenOffer> openOffersList = new ArrayList<>(openOffers);
|
||||
for (int i = 0; i < size; i++) {
|
||||
// we delay to avoid reaching throttle limits
|
||||
// roughly 1 offer per second
|
||||
// roughly 2 offer2 per second
|
||||
final int n = i;
|
||||
final long minDelay = i * 500 + 1;
|
||||
final long maxDelay = minDelay * 2 + 500;
|
||||
final long minDelay = i * 250 + 1;
|
||||
final long maxDelay = minDelay * 2;
|
||||
UserThread.runAfterRandomDelay(() -> {
|
||||
OpenOffer openOffer = openOffersList.get(n);
|
||||
// we need to check if in the meantime the offer has been removed
|
||||
|
@ -516,9 +525,9 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
}
|
||||
|
||||
private void refreshOffer(OpenOffer openOffer) {
|
||||
offerBookService.refreshOffer(openOffer.getOffer(),
|
||||
offerBookService.refreshTTL(openOffer.getOffer(),
|
||||
() -> log.debug("Successful refreshed TTL for offer"),
|
||||
errorMessage -> log.error("Refresh TTL for offer failed. " + errorMessage));
|
||||
errorMessage -> log.warn(errorMessage));
|
||||
}
|
||||
|
||||
private void restart() {
|
||||
|
|
|
@ -4,6 +4,7 @@ public enum AvailabilityResult {
|
|||
AVAILABLE,
|
||||
OFFER_TAKEN,
|
||||
PRICE_OUT_OF_TOLERANCE,
|
||||
MARKET_PRICE_NOT_AVAILABLE,
|
||||
NO_ARBITRATORS,
|
||||
USER_IGNORED,
|
||||
UNKNOWN_FAILURE
|
||||
|
|
|
@ -44,8 +44,8 @@ public class ProcessOfferAvailabilityResponse extends Task<OfferAvailabilityMode
|
|||
if (offerAvailabilityResponse.isAvailable || offerAvailabilityResponse.availabilityResult == AvailabilityResult.AVAILABLE) {
|
||||
model.offer.setState(Offer.State.AVAILABLE);
|
||||
} else {
|
||||
log.warn("Offer rejected because of: " + offerAvailabilityResponse.availabilityResult);
|
||||
model.offer.setState(Offer.State.NOT_AVAILABLE);
|
||||
failed("Take offer attempt rejected because of: " + offerAvailabilityResponse.availabilityResult);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -111,8 +111,7 @@ public final class Preferences implements Persistable {
|
|||
private boolean autoSelectArbitrators = true;
|
||||
private final Map<String, Boolean> dontShowAgainMap;
|
||||
private boolean tacAccepted;
|
||||
// Don't remove as we don't want to break old serialized data
|
||||
private boolean useTorForBitcoinJ = false;
|
||||
private boolean useTorForBitcoinJ = true;
|
||||
private boolean showOwnOffersInOfferBook = true;
|
||||
private Locale preferredLocale;
|
||||
private TradeCurrency preferredTradeCurrency;
|
||||
|
@ -185,8 +184,7 @@ public final class Preferences implements Persistable {
|
|||
defaultLocale = preferredLocale;
|
||||
preferredTradeCurrency = persisted.getPreferredTradeCurrency();
|
||||
defaultTradeCurrency = preferredTradeCurrency;
|
||||
// useTorForBitcoinJ = persisted.getUseTorForBitcoinJ();
|
||||
useTorForBitcoinJ = false;
|
||||
useTorForBitcoinJ = persisted.getUseTorForBitcoinJ();
|
||||
useStickyMarketPrice = persisted.getUseStickyMarketPrice();
|
||||
usePercentageBasedPrice = persisted.getUsePercentageBasedPrice();
|
||||
showOwnOffersInOfferBook = persisted.getShowOwnOffersInOfferBook();
|
||||
|
@ -372,10 +370,10 @@ public final class Preferences implements Persistable {
|
|||
storage.queueUpForSave();
|
||||
}
|
||||
|
||||
/* public void setUseTorForBitcoinJ(boolean useTorForBitcoinJ) {
|
||||
public void setUseTorForBitcoinJ(boolean useTorForBitcoinJ) {
|
||||
this.useTorForBitcoinJ = useTorForBitcoinJ;
|
||||
storage.queueUpForSave();
|
||||
}*/
|
||||
}
|
||||
|
||||
public void setShowOwnOffersInOfferBook(boolean showOwnOffersInOfferBook) {
|
||||
this.showOwnOffersInOfferBook = showOwnOffersInOfferBook;
|
||||
|
|
|
@ -24,7 +24,7 @@ import com.google.inject.Injector;
|
|||
import io.bitsquare.alert.AlertManager;
|
||||
import io.bitsquare.arbitration.ArbitratorManager;
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.common.OptionKeys;
|
||||
import io.bitsquare.common.CommonOptionKeys;
|
||||
import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
import io.bitsquare.common.util.Utilities;
|
||||
|
@ -75,6 +75,7 @@ import java.nio.file.Paths;
|
|||
import java.security.Security;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static io.bitsquare.app.BitsquareEnvironment.APP_NAME_KEY;
|
||||
|
||||
|
@ -108,7 +109,7 @@ public class BitsquareApp extends Application {
|
|||
log.info("Log files under: " + logPath);
|
||||
Version.printVersion();
|
||||
Utilities.printSysInfo();
|
||||
Log.setLevel(Level.toLevel(env.getRequiredProperty(OptionKeys.LOG_LEVEL_KEY)));
|
||||
Log.setLevel(Level.toLevel(env.getRequiredProperty(CommonOptionKeys.LOG_LEVEL_KEY)));
|
||||
|
||||
UserThread.setExecutor(Platform::runLater);
|
||||
UserThread.setTimerClass(UITimer.class);
|
||||
|
@ -345,21 +346,25 @@ public class BitsquareApp extends Application {
|
|||
|
||||
@Override
|
||||
public void stop() {
|
||||
shutDownRequested = true;
|
||||
gracefulShutDown(() -> {
|
||||
log.info("App shutdown complete");
|
||||
System.exit(0);
|
||||
});
|
||||
if (!shutDownRequested) {
|
||||
new Popup().headLine("Shut down in progress")
|
||||
.backgroundInfo("Shutting down application can take a few seconds.\n" +
|
||||
"Please don't interrupt that process.")
|
||||
.hideCloseButton()
|
||||
.useAnimation(false)
|
||||
.show();
|
||||
UserThread.runAfter(() -> {
|
||||
gracefulShutDown(() -> {
|
||||
log.info("App shutdown complete");
|
||||
System.exit(0);
|
||||
});
|
||||
}, 200, TimeUnit.MILLISECONDS);
|
||||
shutDownRequested = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void gracefulShutDown(ResultHandler resultHandler) {
|
||||
log.debug("gracefulShutDown");
|
||||
new Popup().headLine("Shut down in progress")
|
||||
.backgroundInfo("Shutting down application can take a few seconds.\n" +
|
||||
"Please don't interrupt that process.")
|
||||
.hideCloseButton()
|
||||
.useAnimation(false)
|
||||
.show();
|
||||
try {
|
||||
if (injector != null) {
|
||||
injector.getInstance(ArbitratorManager.class).shutDown();
|
||||
|
@ -374,8 +379,8 @@ public class BitsquareApp extends Application {
|
|||
injector.getInstance(WalletService.class).shutDown();
|
||||
});
|
||||
});
|
||||
// we wait max 5 sec.
|
||||
UserThread.runAfter(resultHandler::handleResult, 5);
|
||||
// we wait max 20 sec.
|
||||
UserThread.runAfter(resultHandler::handleResult, 20);
|
||||
} else {
|
||||
UserThread.runAfter(resultHandler::handleResult, 1);
|
||||
}
|
||||
|
|
|
@ -17,23 +17,12 @@
|
|||
|
||||
package io.bitsquare.app;
|
||||
|
||||
import io.bitsquare.BitsquareException;
|
||||
import io.bitsquare.btc.BitcoinNetwork;
|
||||
import io.bitsquare.btc.RegTestHost;
|
||||
import io.bitsquare.network.OptionKeys;
|
||||
import io.bitsquare.p2p.P2PService;
|
||||
import io.bitsquare.util.joptsimple.EnumValueConverter;
|
||||
import joptsimple.OptionException;
|
||||
import joptsimple.OptionParser;
|
||||
import joptsimple.OptionSet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static io.bitsquare.app.BitsquareEnvironment.*;
|
||||
|
||||
public class BitsquareAppMain extends BitsquareExecutable {
|
||||
|
@ -62,7 +51,7 @@ public class BitsquareAppMain extends BitsquareExecutable {
|
|||
BitsquareEnvironment bitsquareEnvironment = new BitsquareEnvironment(options);
|
||||
|
||||
// need to call that before BitsquareAppMain().execute(args)
|
||||
initAppDir(bitsquareEnvironment.getProperty(BitsquareEnvironment.APP_DATA_DIR_KEY));
|
||||
BitsquareExecutable.initAppDir(bitsquareEnvironment.getProperty(BitsquareEnvironment.APP_DATA_DIR_KEY));
|
||||
|
||||
// For some reason the JavaFX launch process results in us losing the thread context class loader: reset it.
|
||||
// In order to work around a bug in JavaFX 8u25 and below, you must include the following code as the first line of your realMain method:
|
||||
|
@ -71,61 +60,6 @@ public class BitsquareAppMain extends BitsquareExecutable {
|
|||
new BitsquareAppMain().execute(args);
|
||||
}
|
||||
|
||||
private static void initAppDir(String appDir) {
|
||||
Path dir = Paths.get(appDir);
|
||||
if (Files.exists(dir)) {
|
||||
if (!Files.isWritable(dir))
|
||||
throw new BitsquareException("Application data directory '%s' is not writeable", dir);
|
||||
else
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Files.createDirectory(dir);
|
||||
} catch (IOException ex) {
|
||||
throw new BitsquareException(ex, "Application data directory '%s' could not be created", dir);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void customizeOptionParsing(OptionParser parser) {
|
||||
parser.accepts(USER_DATA_DIR_KEY, description("User data directory", DEFAULT_USER_DATA_DIR))
|
||||
.withRequiredArg();
|
||||
parser.accepts(APP_NAME_KEY, description("Application name", DEFAULT_APP_NAME))
|
||||
.withRequiredArg();
|
||||
parser.accepts(APP_DATA_DIR_KEY, description("Application data directory", DEFAULT_APP_DATA_DIR))
|
||||
.withRequiredArg();
|
||||
parser.accepts(io.bitsquare.common.OptionKeys.LOG_LEVEL_KEY, description("Log level [OFF, ALL, ERROR, WARN, INFO, DEBUG, TRACE]", LOG_LEVEL_DEFAULT))
|
||||
.withRequiredArg();
|
||||
|
||||
parser.accepts(OptionKeys.SEED_NODES_KEY, description("Override hard coded seed nodes as comma separated list: E.g. rxdkppp3vicnbgqt.onion:8002, mfla72c4igh5ta2t.onion:8002", ""))
|
||||
.withRequiredArg();
|
||||
|
||||
parser.accepts(io.bitsquare.common.OptionKeys.IGNORE_DEV_MSG_KEY, description("If set to true all signed messages from Bitsquare developers are ignored " +
|
||||
"(Global alert, Version update alert, Filters for offers, nodes or payment account data)", false))
|
||||
.withRequiredArg()
|
||||
.ofType(boolean.class);
|
||||
|
||||
// use a fixed port as arbitrator use that for his ID
|
||||
parser.accepts(OptionKeys.PORT_KEY, description("Port to listen on", 9999))
|
||||
.withRequiredArg()
|
||||
.ofType(int.class);
|
||||
parser.accepts(OptionKeys.USE_LOCALHOST, description("Use localhost network for development", false))
|
||||
.withRequiredArg()
|
||||
.ofType(boolean.class);
|
||||
parser.accepts(OptionKeys.MAX_CONNECTIONS, description("Max. connections a peer will try to keep", P2PService.MAX_CONNECTIONS_DEFAULT))
|
||||
.withRequiredArg()
|
||||
.ofType(int.class);
|
||||
parser.accepts(BitcoinNetwork.KEY, description("Bitcoin network", BitcoinNetwork.DEFAULT))
|
||||
.withRequiredArg()
|
||||
.ofType(BitcoinNetwork.class)
|
||||
.withValuesConvertedBy(new EnumValueConverter(BitcoinNetwork.class));
|
||||
|
||||
parser.accepts(RegTestHost.KEY, description("", RegTestHost.DEFAULT))
|
||||
.withRequiredArg()
|
||||
.ofType(RegTestHost.class)
|
||||
.withValuesConvertedBy(new EnumValueConverter(RegTestHost.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(OptionSet options) {
|
||||
BitsquareApp.setEnvironment(new BitsquareEnvironment(options));
|
||||
|
|
|
@ -27,7 +27,7 @@ bg color of non edit textFields: fafafa
|
|||
-bs-content-bg-grey: #f4f4f4;
|
||||
-bs-very-light-grey: #f8f8f8;
|
||||
|
||||
-fx-accent: #0f87c3;
|
||||
-fx-accent: #0f86c3;
|
||||
-bs-blue-soft: derive(-fx-accent, 60%);
|
||||
-bs-blue-transparent: #0f87c344;
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
package io.bitsquare.gui.main;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;
|
||||
import io.bitsquare.alert.Alert;
|
||||
import io.bitsquare.alert.AlertManager;
|
||||
import io.bitsquare.alert.PrivateNotification;
|
||||
|
@ -55,6 +56,7 @@ import io.bitsquare.gui.main.overlays.windows.WalletPasswordWindow;
|
|||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.locale.CurrencyUtil;
|
||||
import io.bitsquare.locale.TradeCurrency;
|
||||
import io.bitsquare.p2p.NodeAddress;
|
||||
import io.bitsquare.p2p.P2PService;
|
||||
import io.bitsquare.p2p.P2PServiceListener;
|
||||
import io.bitsquare.p2p.network.CloseConnectionReason;
|
||||
|
@ -234,8 +236,8 @@ public class MainViewModel implements ViewModel {
|
|||
showStartupTimeoutPopup();
|
||||
}, 4, TimeUnit.MINUTES);
|
||||
|
||||
walletInitialized = initBitcoinWallet();
|
||||
p2pNetWorkReady = initP2PNetwork();
|
||||
walletInitialized = initBitcoinWallet();
|
||||
|
||||
// need to store it to not get garbage collected
|
||||
allServicesDone = EasyBind.combine(walletInitialized, p2pNetWorkReady, (a, b) -> a && b);
|
||||
|
@ -348,6 +350,9 @@ public class MainViewModel implements ViewModel {
|
|||
public void onTorNodeReady() {
|
||||
bootstrapState.set("Tor node created");
|
||||
p2PNetworkIconId.set("image-connection-tor");
|
||||
if( preferences.getUseTorForBitcoinJ() ) {
|
||||
initWalletService();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -422,6 +427,18 @@ public class MainViewModel implements ViewModel {
|
|||
}
|
||||
|
||||
private BooleanProperty initBitcoinWallet() {
|
||||
final BooleanProperty walletInitialized = new SimpleBooleanProperty();
|
||||
|
||||
// We only init wallet service here if not using Tor for bitcoinj.
|
||||
// When using Tor, wallet init must be deferred until Tor is ready.
|
||||
if( !preferences.getUseTorForBitcoinJ() ) {
|
||||
initWalletService();
|
||||
}
|
||||
|
||||
return walletInitialized;
|
||||
}
|
||||
|
||||
private void initWalletService() {
|
||||
ObjectProperty<Throwable> walletServiceException = new SimpleObjectProperty<>();
|
||||
btcInfoBinding = EasyBind.combine(walletService.downloadPercentageProperty(), walletService.numPeersProperty(), walletServiceException,
|
||||
(downloadPercentage, numPeers, exception) -> {
|
||||
|
@ -461,9 +478,26 @@ public class MainViewModel implements ViewModel {
|
|||
btcInfoBinding.subscribe((observable, oldValue, newValue) -> {
|
||||
btcInfo.set(newValue);
|
||||
});
|
||||
|
||||
Socks5Proxy proxy = null;
|
||||
|
||||
if( preferences.getUseTorForBitcoinJ() ) {
|
||||
// Use p2p service
|
||||
proxy = p2PService.getNetworkNode().getSocksProxy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Uncomment this to wire up user specified proxy via program args or config file.
|
||||
* Could be Tor, i2p, ssh, vpn, etc.
|
||||
if( preferences.getBitcoinProxyHost() != null &&
|
||||
preferences.getBitcoinProxyPort() != null ) {
|
||||
proxy = new Socks5Proxy( preferences.getBitcoinProxyHost(),
|
||||
preferences.getBitcoinProxyPort() );
|
||||
}
|
||||
*/
|
||||
|
||||
final BooleanProperty walletInitialized = new SimpleBooleanProperty();
|
||||
walletService.initialize(null,
|
||||
proxy,
|
||||
() -> {
|
||||
numBtcPeers = walletService.numPeersProperty().get();
|
||||
|
||||
|
@ -473,8 +507,8 @@ public class MainViewModel implements ViewModel {
|
|||
|
||||
walletPasswordWindow
|
||||
.onAesKey(aesKey -> {
|
||||
tradeWalletService.setAesKey(aesKey);
|
||||
walletService.setAesKey(aesKey);
|
||||
tradeWalletService.setAesKey(aesKey);
|
||||
walletInitialized.set(true);
|
||||
})
|
||||
.hideCloseButton()
|
||||
|
@ -484,7 +518,6 @@ public class MainViewModel implements ViewModel {
|
|||
}
|
||||
},
|
||||
walletServiceException::set);
|
||||
return walletInitialized;
|
||||
}
|
||||
|
||||
private void onAllServicesInitialized() {
|
||||
|
@ -528,6 +561,8 @@ public class MainViewModel implements ViewModel {
|
|||
privateNotificationManager.privateNotificationProperty().addListener((observable, oldValue, newValue) -> displayPrivateNotification(newValue));
|
||||
displayAlertIfPresent(alertManager.alertMessageProperty().get());
|
||||
|
||||
p2PService.onAllServicesInitialized();
|
||||
|
||||
setupBtcNumPeersWatcher();
|
||||
setupP2PNumPeersWatcher();
|
||||
updateBalance();
|
||||
|
|
|
@ -20,6 +20,7 @@ package io.bitsquare.gui.main.account.content.altcoinaccounts;
|
|||
import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.common.util.Tuple2;
|
||||
import io.bitsquare.common.util.Tuple3;
|
||||
import io.bitsquare.common.util.Utilities;
|
||||
import io.bitsquare.gui.common.view.ActivatableViewAndModel;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.components.TitledGroupBg;
|
||||
|
@ -48,6 +49,8 @@ import javafx.scene.layout.GridPane;
|
|||
import javafx.util.Callback;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static io.bitsquare.gui.util.FormBuilder.*;
|
||||
|
@ -164,6 +167,36 @@ public class AltCoinAccountsView extends ActivatableViewAndModel<GridPane, AltCo
|
|||
"If you are not sure about that process visit the Monero forum (https://forum.getmonero.org) to find more information.")
|
||||
.closeButtonText("I understand")
|
||||
.show();
|
||||
} else if (code.equals("ETHC")) {
|
||||
//TODO remove after JULY, 21
|
||||
if (new Date().before(new Date(2016 - 1900, Calendar.JULY, 21))) {
|
||||
new Popup().information("You cannot use EtherClassic before the hard fork gets activated.\n" +
|
||||
"It is planned for July, 20 2016, but please check on their project web page for detailed information.\n\n" +
|
||||
"The EHT/ETHC fork situation carries considerable risks.\n" +
|
||||
"Be sure you fully understand the situation and check out the information on the \"Ethereum Classic\" and \"Ethereum\" project web pages.")
|
||||
.closeButtonText("I understand")
|
||||
.onAction(() -> Utilities.openWebPage("https://ethereumclassic.github.io/"))
|
||||
.actionButtonText("Open Ethereum Classic web page")
|
||||
.show();
|
||||
} else if (new Date().before(new Date(2016 - 1900, Calendar.AUGUST, 30))) {
|
||||
//TODO remove after AUGUST, 30
|
||||
new Popup().information("The EHT/ETHC fork situation carries considerable risks.\n" +
|
||||
"Be sure you fully understand the situation and check out the information on the \"Ethereum Classic\" and \"Ethereum\" project web pages.")
|
||||
.closeButtonText("I understand")
|
||||
.onAction(() -> Utilities.openWebPage("https://ethereumclassic.github.io/"))
|
||||
.actionButtonText("Open Ethereum Classic web page")
|
||||
.show();
|
||||
}
|
||||
} else if (code.equals("ETH")) {
|
||||
//TODO remove after AUGUST, 30
|
||||
if (new Date().before(new Date(2016 - 1900, Calendar.AUGUST, 30))) {
|
||||
new Popup().information("The EHT/ETHC fork situation carries considerable risks.\n" +
|
||||
"Be sure you fully understand the situation and check out the information on the \"Ethereum Classic\" and \"Ethereum\" project web pages.")
|
||||
.closeButtonText("I understand")
|
||||
.onAction(() -> Utilities.openWebPage("https://www.ethereum.org/"))
|
||||
.actionButtonText("Open Ethereum web page")
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
if (!model.getPaymentAccounts().stream().filter(e -> {
|
||||
|
|
|
@ -204,8 +204,8 @@ public class ArbitratorSelectionView extends ActivatableViewAndModel<GridPane, A
|
|||
TableColumn<ArbitratorListItem, String> dateColumn = new TableColumn("Registration date");
|
||||
dateColumn.setSortable(false);
|
||||
dateColumn.setCellValueFactory(param -> new ReadOnlyObjectWrapper(param.getValue().getRegistrationDate()));
|
||||
dateColumn.setMinWidth(130);
|
||||
dateColumn.setMaxWidth(130);
|
||||
dateColumn.setMinWidth(140);
|
||||
dateColumn.setMaxWidth(140);
|
||||
|
||||
TableColumn<ArbitratorListItem, String> nameColumn = new TableColumn("Onion address");
|
||||
nameColumn.setSortable(false);
|
||||
|
|
|
@ -113,9 +113,8 @@ public class PasswordView extends ActivatableView<GridPane, Void> {
|
|||
|
||||
if (wallet.isEncrypted()) {
|
||||
if (wallet.checkAESKey(aesKey)) {
|
||||
wallet.decrypt(aesKey);
|
||||
walletService.decryptWallet(aesKey);
|
||||
tradeWalletService.setAesKey(null);
|
||||
walletService.setAesKey(null);
|
||||
new Popup()
|
||||
.feedback("Wallet successfully decrypted and password protection removed.")
|
||||
.show();
|
||||
|
@ -130,10 +129,9 @@ public class PasswordView extends ActivatableView<GridPane, Void> {
|
|||
.show();
|
||||
}
|
||||
} else {
|
||||
wallet.encrypt(keyCrypterScrypt, aesKey);
|
||||
// we save the key for the trade wallet as we don't require passwords here
|
||||
walletService.encryptWallet(keyCrypterScrypt, aesKey);
|
||||
tradeWalletService.setAesKey(aesKey);
|
||||
walletService.setAesKey(aesKey);
|
||||
new Popup()
|
||||
.feedback("Wallet successfully encrypted and password protection enabled.")
|
||||
.show();
|
||||
|
|
|
@ -268,7 +268,7 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
|
|||
DeterministicSeed seed = new DeterministicSeed(Splitter.on(" ").splitToList(restoreSeedWordsTextArea.getText()), null, "", date);
|
||||
walletService.restoreSeedWords(seed,
|
||||
() -> UserThread.execute(() -> {
|
||||
log.debug("Wallet restored with seed words");
|
||||
log.info("Wallet restored with seed words");
|
||||
|
||||
new Popup()
|
||||
.feedback("Wallet restored successfully with the new seed words.\n\n" +
|
||||
|
|
|
@ -10,13 +10,17 @@ import javax.annotation.Nullable;
|
|||
public class MarketStatisticItem {
|
||||
private static final Logger log = LoggerFactory.getLogger(MarketStatisticItem.class);
|
||||
public final String currencyCode;
|
||||
public final int numberOfBuyOffers;
|
||||
public final int numberOfSellOffers;
|
||||
public final int numberOfOffers;
|
||||
@Nullable
|
||||
public final Fiat spread;
|
||||
public final Coin totalAmount;
|
||||
|
||||
public MarketStatisticItem(String currencyCode, int numberOfOffers, @Nullable Fiat spread, Coin totalAmount) {
|
||||
public MarketStatisticItem(String currencyCode, int numberOfBuyOffers, int numberOfSellOffers, int numberOfOffers, @Nullable Fiat spread, Coin totalAmount) {
|
||||
this.currencyCode = currencyCode;
|
||||
this.numberOfBuyOffers = numberOfBuyOffers;
|
||||
this.numberOfSellOffers = numberOfSellOffers;
|
||||
this.numberOfOffers = numberOfOffers;
|
||||
this.spread = spread;
|
||||
this.totalAmount = totalAmount;
|
||||
|
|
|
@ -109,7 +109,7 @@ class MarketsStatisticViewModel extends ActivatableViewModel {
|
|||
spread = bestSellOfferPrice.subtract(bestBuyOfferPrice);
|
||||
|
||||
Coin totalAmount = Coin.valueOf(offers.stream().mapToLong(offer -> offer.getAmount().getValue()).sum());
|
||||
marketStatisticItems.add(new MarketStatisticItem(currencyCode, offers.size(), spread, totalAmount));
|
||||
marketStatisticItems.add(new MarketStatisticItem(currencyCode, buyOffers.size(), sellOffers.size(), offers.size(), spread, totalAmount));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,8 +44,7 @@ public class MarketsStatisticsView extends ActivatableViewAndModel<GridPane, Mar
|
|||
private TableView<MarketStatisticItem> tableView;
|
||||
private SortedList<MarketStatisticItem> sortedList;
|
||||
private ListChangeListener<MarketStatisticItem> itemListChangeListener;
|
||||
private TableColumn<MarketStatisticItem, MarketStatisticItem> totalAmountColumn;
|
||||
private TableColumn<MarketStatisticItem, MarketStatisticItem> numberOfOffersColumn;
|
||||
private TableColumn<MarketStatisticItem, MarketStatisticItem> totalAmountColumn, numberOfOffersColumn, numberOfBuyOffersColumn, numberOfSellOffersColumn;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -79,6 +78,10 @@ public class MarketsStatisticsView extends ActivatableViewAndModel<GridPane, Mar
|
|||
tableView.getColumns().add(currencyColumn);
|
||||
numberOfOffersColumn = getNumberOfOffersColumn();
|
||||
tableView.getColumns().add(numberOfOffersColumn);
|
||||
numberOfBuyOffersColumn = getNumberOfBuyOffersColumn();
|
||||
tableView.getColumns().add(numberOfBuyOffersColumn);
|
||||
numberOfSellOffersColumn = getNumberOfSellOffersColumn();
|
||||
tableView.getColumns().add(numberOfSellOffersColumn);
|
||||
totalAmountColumn = getTotalAmountColumn();
|
||||
tableView.getColumns().add(totalAmountColumn);
|
||||
TableColumn<MarketStatisticItem, MarketStatisticItem> spreadColumn = getSpreadColumn();
|
||||
|
@ -87,6 +90,8 @@ public class MarketsStatisticsView extends ActivatableViewAndModel<GridPane, Mar
|
|||
|
||||
currencyColumn.setComparator((o1, o2) -> CurrencyUtil.getNameByCode(o1.currencyCode).compareTo(CurrencyUtil.getNameByCode(o2.currencyCode)));
|
||||
numberOfOffersColumn.setComparator((o1, o2) -> Integer.valueOf(o1.numberOfOffers).compareTo(o2.numberOfOffers));
|
||||
numberOfBuyOffersColumn.setComparator((o1, o2) -> Integer.valueOf(o1.numberOfBuyOffers).compareTo(o2.numberOfBuyOffers));
|
||||
numberOfSellOffersColumn.setComparator((o1, o2) -> Integer.valueOf(o1.numberOfSellOffers).compareTo(o2.numberOfSellOffers));
|
||||
totalAmountColumn.setComparator((o1, o2) -> o1.totalAmount.compareTo(o2.totalAmount));
|
||||
spreadColumn.setComparator((o1, o2) -> o1.spread != null && o2.spread != null ? formatter.formatFiatWithCode(o1.spread).compareTo(formatter.formatFiatWithCode(o2.spread)) : 0);
|
||||
|
||||
|
@ -112,6 +117,8 @@ public class MarketsStatisticsView extends ActivatableViewAndModel<GridPane, Mar
|
|||
|
||||
private void updateHeaders() {
|
||||
numberOfOffersColumn.setText("Total offers (" + sortedList.stream().mapToInt(item -> item.numberOfOffers).sum() + ")");
|
||||
numberOfBuyOffersColumn.setText("Bid offers (" + sortedList.stream().mapToInt(item -> item.numberOfBuyOffers).sum() + ")");
|
||||
numberOfSellOffersColumn.setText("Ask offers (" + sortedList.stream().mapToInt(item -> item.numberOfSellOffers).sum() + ")");
|
||||
totalAmountColumn.setText("Total amount (" + formatter.formatCoinWithCode(Coin.valueOf(sortedList.stream().mapToLong(item -> item.totalAmount.value).sum())) + ")");
|
||||
}
|
||||
|
||||
|
@ -176,10 +183,66 @@ public class MarketsStatisticsView extends ActivatableViewAndModel<GridPane, Mar
|
|||
return column;
|
||||
}
|
||||
|
||||
private TableColumn<MarketStatisticItem, MarketStatisticItem> getNumberOfBuyOffersColumn() {
|
||||
TableColumn<MarketStatisticItem, MarketStatisticItem> column = new TableColumn<MarketStatisticItem, MarketStatisticItem>("Buy offers") {
|
||||
{
|
||||
setMinWidth(100);
|
||||
}
|
||||
};
|
||||
column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
|
||||
column.setCellFactory(
|
||||
new Callback<TableColumn<MarketStatisticItem, MarketStatisticItem>, TableCell<MarketStatisticItem,
|
||||
MarketStatisticItem>>() {
|
||||
@Override
|
||||
public TableCell<MarketStatisticItem, MarketStatisticItem> call(
|
||||
TableColumn<MarketStatisticItem, MarketStatisticItem> column) {
|
||||
return new TableCell<MarketStatisticItem, MarketStatisticItem>() {
|
||||
@Override
|
||||
public void updateItem(final MarketStatisticItem item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (item != null && !empty)
|
||||
setText(String.valueOf(item.numberOfBuyOffers));
|
||||
else
|
||||
setText("");
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
return column;
|
||||
}
|
||||
|
||||
private TableColumn<MarketStatisticItem, MarketStatisticItem> getNumberOfSellOffersColumn() {
|
||||
TableColumn<MarketStatisticItem, MarketStatisticItem> column = new TableColumn<MarketStatisticItem, MarketStatisticItem>("Sell offers") {
|
||||
{
|
||||
setMinWidth(100);
|
||||
}
|
||||
};
|
||||
column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
|
||||
column.setCellFactory(
|
||||
new Callback<TableColumn<MarketStatisticItem, MarketStatisticItem>, TableCell<MarketStatisticItem,
|
||||
MarketStatisticItem>>() {
|
||||
@Override
|
||||
public TableCell<MarketStatisticItem, MarketStatisticItem> call(
|
||||
TableColumn<MarketStatisticItem, MarketStatisticItem> column) {
|
||||
return new TableCell<MarketStatisticItem, MarketStatisticItem>() {
|
||||
@Override
|
||||
public void updateItem(final MarketStatisticItem item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (item != null && !empty)
|
||||
setText(String.valueOf(item.numberOfSellOffers));
|
||||
else
|
||||
setText("");
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
return column;
|
||||
}
|
||||
|
||||
private TableColumn<MarketStatisticItem, MarketStatisticItem> getTotalAmountColumn() {
|
||||
TableColumn<MarketStatisticItem, MarketStatisticItem> column = new TableColumn<MarketStatisticItem, MarketStatisticItem>("Total amount") {
|
||||
{
|
||||
setMinWidth(130);
|
||||
setMinWidth(150);
|
||||
}
|
||||
};
|
||||
column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
|
||||
|
|
|
@ -22,6 +22,7 @@ import io.bitsquare.btc.pricefeed.MarketPrice;
|
|||
import io.bitsquare.btc.pricefeed.PriceFeed;
|
||||
import io.bitsquare.common.Timer;
|
||||
import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.common.util.Utilities;
|
||||
import io.bitsquare.gui.Navigation;
|
||||
import io.bitsquare.gui.common.model.ActivatableWithDataModel;
|
||||
import io.bitsquare.gui.common.model.ViewModel;
|
||||
|
@ -49,6 +50,8 @@ import org.bitcoinj.core.Coin;
|
|||
import org.bitcoinj.utils.Fiat;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
import static com.google.common.math.LongMath.checkedPow;
|
||||
import static javafx.beans.binding.Bindings.createStringBinding;
|
||||
|
@ -127,6 +130,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
private PriceFeed.Type priceFeedType;
|
||||
private boolean inputIsMarketBasedPrice;
|
||||
private ChangeListener<Boolean> useMarketBasedPriceListener;
|
||||
private ChangeListener<String> currencyCodeListener;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -186,6 +190,9 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
directionLabel = BSResources.get("shared.sellBitcoin");
|
||||
amountDescription = BSResources.get("createOffer.amountPriceBox.amountDescription", BSResources.get("shared.sell"));
|
||||
}
|
||||
|
||||
//TODO remove after AUGUST, 30
|
||||
applyCurrencyCode(dataModel.getTradeCurrency().getCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -336,6 +343,34 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
/* feeFromFundingTxListener = (ov, oldValue, newValue) -> {
|
||||
updateButtonDisableState();
|
||||
};*/
|
||||
|
||||
|
||||
currencyCodeListener = (observable, oldValue, newValue) -> applyCurrencyCode(newValue);
|
||||
}
|
||||
|
||||
//TODO remove after AUGUST, 30
|
||||
private void applyCurrencyCode(String newValue) {
|
||||
String key = "ETH-ETHC-Warning";
|
||||
if (preferences.showAgain(key) && new Date().before(new Date(2016 - 1900, Calendar.AUGUST, 30))) {
|
||||
if (newValue.equals("ETH")) {
|
||||
new Popup().information("The EHT/ETHC fork situation carries considerable risks.\n" +
|
||||
"Be sure you fully understand the situation and check out the information on the \"Ethereum Classic\" and \"Ethereum\" project web pages.")
|
||||
.closeButtonText("I understand")
|
||||
.onAction(() -> Utilities.openWebPage("https://www.ethereum.org/"))
|
||||
.actionButtonText("Open Ethereum web page")
|
||||
.dontShowAgainId(key, preferences)
|
||||
.show();
|
||||
} else if (newValue.equals("ETHC")) {
|
||||
new Popup().information("The EHT/ETHC fork situation carries considerable risks.\n" +
|
||||
"Be sure you fully understand the situation and check out the information on the \"Ethereum Classic\" and \"Ethereum\" project web pages.\n\n" +
|
||||
"Please note, that the price is denominated as ETHC/BTC not BTC/ETHC!")
|
||||
.closeButtonText("I understand")
|
||||
.onAction(() -> Utilities.openWebPage("https://ethereumclassic.github.io/"))
|
||||
.actionButtonText("Open Ethereum Classic web page")
|
||||
.dontShowAgainId(key, preferences)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addListeners() {
|
||||
|
@ -356,6 +391,9 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
|
||||
// dataModel.feeFromFundingTxProperty.addListener(feeFromFundingTxListener);
|
||||
dataModel.isWalletFunded.addListener(isWalletFundedListener);
|
||||
|
||||
//TODO remove after AUGUST, 30
|
||||
dataModel.tradeCurrencyCode.addListener(currencyCodeListener);
|
||||
}
|
||||
|
||||
private void removeListeners() {
|
||||
|
@ -377,6 +415,9 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
|
||||
if (offer != null && errorMessageListener != null)
|
||||
offer.errorMessageProperty().removeListener(errorMessageListener);
|
||||
|
||||
//TODO remove after AUGUST, 30
|
||||
dataModel.tradeCurrencyCode.removeListener(currencyCodeListener);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -460,7 +460,7 @@ class OfferBookViewModel extends ActivatableViewModel {
|
|||
}
|
||||
|
||||
boolean isIgnored(Offer offer) {
|
||||
return preferences.getIgnoreTradersList().stream().filter(i -> i.equals(offer.getOffererNodeAddress().hostName)).findAny().isPresent();
|
||||
return preferences.getIgnoreTradersList().stream().filter(i -> i.equals(offer.getOffererNodeAddress().getHostNameWithoutPostFix())).findAny().isPresent();
|
||||
}
|
||||
|
||||
boolean isOfferBanned(Offer offer) {
|
||||
|
@ -474,7 +474,7 @@ class OfferBookViewModel extends ActivatableViewModel {
|
|||
boolean isNodeBanned(Offer offer) {
|
||||
return filterManager.getFilter() != null &&
|
||||
filterManager.getFilter().bannedNodeAddress.stream()
|
||||
.filter(e -> e.equals(offer.getOffererNodeAddress().hostName))
|
||||
.filter(e -> e.equals(offer.getOffererNodeAddress().getHostNameWithoutPostFix()))
|
||||
.findAny()
|
||||
.isPresent();
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import io.bitsquare.btc.listeners.BalanceListener;
|
|||
import io.bitsquare.btc.pricefeed.PriceFeed;
|
||||
import io.bitsquare.gui.common.model.ActivatableDataModel;
|
||||
import io.bitsquare.gui.main.overlays.notifications.Notification;
|
||||
import io.bitsquare.gui.main.overlays.popups.Popup;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.locale.CurrencyUtil;
|
||||
import io.bitsquare.locale.TradeCurrency;
|
||||
|
@ -140,8 +141,10 @@ class TakeOfferDataModel extends ActivatableDataModel {
|
|||
if (!preferences.getUseStickyMarketPrice() && isTabSelected)
|
||||
priceFeed.setCurrencyCode(offer.getCurrencyCode());
|
||||
|
||||
tradeManager.checkOfferAvailability(offer, () -> {
|
||||
});
|
||||
tradeManager.checkOfferAvailability(offer,
|
||||
() -> {
|
||||
},
|
||||
errorMessage -> new Popup().warning(errorMessage).show());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -234,7 +237,11 @@ class TakeOfferDataModel extends ActivatableDataModel {
|
|||
offer,
|
||||
paymentAccount.getId(),
|
||||
useSavingsWallet,
|
||||
tradeResultHandler
|
||||
tradeResultHandler,
|
||||
errorMessage -> {
|
||||
log.warn(errorMessage);
|
||||
new Popup<>().warning(errorMessage).show();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ package io.bitsquare.gui.main.offer.takeoffer;
|
|||
|
||||
import io.bitsquare.arbitration.Arbitrator;
|
||||
import io.bitsquare.btc.pricefeed.PriceFeed;
|
||||
import io.bitsquare.common.util.Utilities;
|
||||
import io.bitsquare.gui.Navigation;
|
||||
import io.bitsquare.gui.common.model.ActivatableWithDataModel;
|
||||
import io.bitsquare.gui.common.model.ViewModel;
|
||||
|
@ -44,6 +45,8 @@ import javafx.collections.ObservableList;
|
|||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
@ -136,6 +139,29 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||
updateButtonDisableState();
|
||||
|
||||
updateSpinnerInfo();
|
||||
|
||||
//TODO remove after AUGUST, 30
|
||||
String key = "ETH-ETHC-Warning";
|
||||
if (dataModel.getPreferences().showAgain(key) && new Date().before(new Date(2016 - 1900, Calendar.AUGUST, 30))) {
|
||||
if (dataModel.getCurrencyCode().equals("ETH")) {
|
||||
new Popup().information("The EHT/ETHC fork situation carries considerable risks.\n" +
|
||||
"Be sure you fully understand the situation and check out the information on the \"Ethereum Classic\" and \"Ethereum\" project web pages.")
|
||||
.closeButtonText("I understand")
|
||||
.onAction(() -> Utilities.openWebPage("https://www.ethereum.org/"))
|
||||
.actionButtonText("Open Ethereum web page")
|
||||
.dontShowAgainId(key, dataModel.getPreferences())
|
||||
.show();
|
||||
} else if (dataModel.getCurrencyCode().equals("ETHC")) {
|
||||
new Popup().information("The EHT/ETHC fork situation carries considerable risks.\n" +
|
||||
"Be sure you fully understand the situation and check out the information on the \"Ethereum Classic\" and \"Ethereum\" project web pages.\n\n" +
|
||||
"Please note, that the price is denominated as ETHC/BTC not BTC/ETHC!")
|
||||
.closeButtonText("I understand")
|
||||
.onAction(() -> Utilities.openWebPage("https://ethereumclassic.github.io/"))
|
||||
.actionButtonText("Open Ethereum Classic web page")
|
||||
.dontShowAgainId(key, dataModel.getPreferences())
|
||||
.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,6 +19,7 @@ package io.bitsquare.gui.main.portfolio.pendingtrades;
|
|||
|
||||
import com.google.inject.Inject;
|
||||
import io.bitsquare.app.Log;
|
||||
import io.bitsquare.arbitration.Arbitrator;
|
||||
import io.bitsquare.arbitration.Dispute;
|
||||
import io.bitsquare.arbitration.DisputeManager;
|
||||
import io.bitsquare.btc.FeePolicy;
|
||||
|
@ -33,8 +34,10 @@ import io.bitsquare.gui.common.model.ActivatableDataModel;
|
|||
import io.bitsquare.gui.main.MainView;
|
||||
import io.bitsquare.gui.main.disputes.DisputesView;
|
||||
import io.bitsquare.gui.main.overlays.notifications.NotificationCenter;
|
||||
import io.bitsquare.gui.main.overlays.popups.Popup;
|
||||
import io.bitsquare.gui.main.overlays.windows.SelectDepositTxWindow;
|
||||
import io.bitsquare.gui.main.overlays.windows.WalletPasswordWindow;
|
||||
import io.bitsquare.p2p.P2PService;
|
||||
import io.bitsquare.payment.PaymentAccountContractData;
|
||||
import io.bitsquare.trade.BuyerTrade;
|
||||
import io.bitsquare.trade.SellerTrade;
|
||||
|
@ -70,6 +73,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
private final User user;
|
||||
private final KeyRing keyRing;
|
||||
public final DisputeManager disputeManager;
|
||||
private P2PService p2PService;
|
||||
public final Navigation navigation;
|
||||
public final WalletPasswordWindow walletPasswordWindow;
|
||||
private final NotificationCenter notificationCenter;
|
||||
|
@ -90,7 +94,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
|
||||
@Inject
|
||||
public PendingTradesDataModel(TradeManager tradeManager, WalletService walletService, TradeWalletService tradeWalletService,
|
||||
User user, KeyRing keyRing, DisputeManager disputeManager, Preferences preferences,
|
||||
User user, KeyRing keyRing, DisputeManager disputeManager, Preferences preferences, P2PService p2PService,
|
||||
Navigation navigation, WalletPasswordWindow walletPasswordWindow, NotificationCenter notificationCenter) {
|
||||
this.tradeManager = tradeManager;
|
||||
this.walletService = walletService;
|
||||
|
@ -99,6 +103,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
this.keyRing = keyRing;
|
||||
this.disputeManager = disputeManager;
|
||||
this.preferences = preferences;
|
||||
this.p2PService = p2PService;
|
||||
this.navigation = navigation;
|
||||
this.walletPasswordWindow = walletPasswordWindow;
|
||||
this.notificationCenter = notificationCenter;
|
||||
|
@ -352,6 +357,8 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
log.debug("payoutTx is null at doOpenDispute");
|
||||
}
|
||||
|
||||
final Arbitrator acceptedArbitratorByAddress = user.getAcceptedArbitratorByAddress(trade.getArbitratorNodeAddress());
|
||||
checkNotNull(acceptedArbitratorByAddress);
|
||||
Dispute dispute = new Dispute(disputeManager.getDisputeStorage(),
|
||||
trade.getId(),
|
||||
keyRing.getPubKeyRing().hashCode(), // traderId
|
||||
|
@ -368,13 +375,19 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
trade.getContractAsJson(),
|
||||
trade.getOffererContractSignature(),
|
||||
trade.getTakerContractSignature(),
|
||||
user.getAcceptedArbitratorByAddress(trade.getArbitratorNodeAddress()).getPubKeyRing(),
|
||||
acceptedArbitratorByAddress.getPubKeyRing(),
|
||||
isSupportTicket
|
||||
);
|
||||
|
||||
trade.setDisputeState(Trade.DisputeState.DISPUTE_REQUESTED);
|
||||
disputeManager.sendOpenNewDisputeMessage(dispute);
|
||||
navigation.navigateTo(MainView.class, DisputesView.class);
|
||||
if (p2PService.isBootstrapped()) {
|
||||
disputeManager.sendOpenNewDisputeMessage(dispute,
|
||||
() -> navigation.navigateTo(MainView.class, DisputesView.class),
|
||||
errorMessage -> new Popup().warning(errorMessage).show());
|
||||
} else {
|
||||
new Popup().information("You need to wait until you are fully connected to the network.\n" +
|
||||
"That might take up to about 2 minutes at startup.").show();
|
||||
}
|
||||
} else {
|
||||
log.warn("trade is null at doOpenDispute");
|
||||
}
|
||||
|
|
|
@ -289,8 +289,8 @@ public abstract class TradeStepView extends AnchorPane {
|
|||
}
|
||||
new Popup().warning(trade.errorMessageProperty().getValue()
|
||||
+ "\n\nPlease report the problem to your arbitrator.\n\n" +
|
||||
"He will forward teh information to the developers to investigate the problem.\n" +
|
||||
"After the problem has be analyzed you will get back all the funds if they are locked.\n" +
|
||||
"He will forward the information to the developers to investigate the problem.\n" +
|
||||
"After the problem has be analyzed you will get back all the funds if funds was locked.\n" +
|
||||
"There will be no arbitration fee charged in case of a software bug.")
|
||||
.show();
|
||||
|
||||
|
|
|
@ -96,9 +96,8 @@ public class BuyerStep2View extends TradeStepView {
|
|||
.attention(message)
|
||||
.show();
|
||||
}
|
||||
} else if (state == Trade.State.BUYER_CONFIRMED_FIAT_PAYMENT_INITIATED) {
|
||||
} else if (state == Trade.State.BUYER_CONFIRMED_FIAT_PAYMENT_INITIATED && confirmButton.isDisabled()) {
|
||||
showStatusInfo();
|
||||
statusLabel.setText("Sending confirmation...");
|
||||
} else if (state == Trade.State.BUYER_SENT_FIAT_PAYMENT_INITIATED_MSG) {
|
||||
hideStatusInfo();
|
||||
}
|
||||
|
@ -245,6 +244,7 @@ public class BuyerStep2View extends TradeStepView {
|
|||
|
||||
private void confirmPaymentStarted() {
|
||||
confirmButton.setDisable(true);
|
||||
showStatusInfo();
|
||||
model.dataModel.onPaymentStarted(() -> {
|
||||
// In case the first send failed we got the support button displayed.
|
||||
// If it succeeds at a second try we remove the support button again.
|
||||
|
@ -261,6 +261,7 @@ public class BuyerStep2View extends TradeStepView {
|
|||
|
||||
private void showStatusInfo() {
|
||||
busyAnimation.play();
|
||||
statusLabel.setText("Sending confirmation...");
|
||||
}
|
||||
|
||||
private void hideStatusInfo() {
|
||||
|
|
|
@ -102,9 +102,8 @@ public class SellerStep3View extends TradeStepView {
|
|||
.show();
|
||||
}
|
||||
|
||||
} else if (state == Trade.State.SELLER_CONFIRMED_FIAT_PAYMENT_RECEIPT) {
|
||||
} else if (state == Trade.State.SELLER_CONFIRMED_FIAT_PAYMENT_RECEIPT && confirmButton.isDisabled()) {
|
||||
showStatusInfo();
|
||||
statusLabel.setText("Sending confirmation...");
|
||||
} else if (state == Trade.State.SELLER_SENT_FIAT_PAYMENT_RECEIPT_MSG) {
|
||||
hideStatusInfo();
|
||||
}
|
||||
|
@ -249,19 +248,21 @@ public class SellerStep3View extends TradeStepView {
|
|||
Preferences preferences = model.dataModel.preferences;
|
||||
String key = "confirmPaymentReceived";
|
||||
if (!DevFlags.DEV_MODE && preferences.showAgain(key)) {
|
||||
PaymentAccountContractData paymentAccountContractData = model.dataModel.getSellersPaymentAccountContractData();
|
||||
String message = "Have you received the " + CurrencyUtil.getNameByCode(model.dataModel.getCurrencyCode()) +
|
||||
" payment from your trading partner?\n\n" +
|
||||
"The trade ID (\"reason for payment\" text) of the transaction is: \"" + trade.getShortId() + "\"\n\n";
|
||||
" payment from your trading partner?\n\n";
|
||||
if (!(paymentAccountContractData instanceof CryptoCurrencyAccountContractData)) {
|
||||
message += "The trade ID (\"reason for payment\" text) of the transaction is: \"" + trade.getShortId() + "\"\n\n";
|
||||
|
||||
Optional<String> optionalHolderName = getOptionalHolderName();
|
||||
if (optionalHolderName.isPresent()) {
|
||||
message = message +
|
||||
"Please also verify that the senders name in your bank statement matches that one from the trade contract:\n" +
|
||||
"Senders name: " + optionalHolderName.get() + "\n\n" +
|
||||
"If the name is not the same as the one displayed here, please don't confirm but open a " +
|
||||
"dispute by entering \"cmd + o\" or \"ctrl + o\".\n\n";
|
||||
Optional<String> optionalHolderName = getOptionalHolderName();
|
||||
if (optionalHolderName.isPresent()) {
|
||||
message += "Please also verify that the senders name in your bank statement matches that one from the trade contract:\n" +
|
||||
"Senders name: " + optionalHolderName.get() + "\n\n" +
|
||||
"If the name is not the same as the one displayed here, please don't confirm but open a " +
|
||||
"dispute by entering \"cmd + o\" or \"ctrl + o\".\n\n";
|
||||
}
|
||||
}
|
||||
message = message + "Please note, that as soon you have confirmed the receipt, the locked trade amount will be released " +
|
||||
message += "Please note, that as soon you have confirmed the receipt, the locked trade amount will be released " +
|
||||
"to the bitcoin buyer and the security deposit will be refunded.";
|
||||
new Popup()
|
||||
.headLine("Confirm that you have received the payment")
|
||||
|
@ -282,6 +283,7 @@ public class SellerStep3View extends TradeStepView {
|
|||
|
||||
private void confirmPaymentReceived() {
|
||||
confirmButton.setDisable(true);
|
||||
showStatusInfo();
|
||||
|
||||
model.dataModel.onFiatPaymentReceived(() -> {
|
||||
// In case the first send failed we got the support button displayed.
|
||||
|
@ -299,6 +301,7 @@ public class SellerStep3View extends TradeStepView {
|
|||
|
||||
private void showStatusInfo() {
|
||||
busyAnimation.play();
|
||||
statusLabel.setText("Sending confirmation...");
|
||||
}
|
||||
|
||||
private void hideStatusInfo() {
|
||||
|
|
|
@ -34,14 +34,14 @@
|
|||
|
||||
<TitledGroupBg text="Bitcoin network" GridPane.rowSpan="2"/>
|
||||
|
||||
<!-- <Label text="Use tor:" GridPane.rowIndex="1"/>
|
||||
<CheckBox fx:id="useTorCheckBox" GridPane.rowIndex="1" GridPane.columnIndex="1"/>
|
||||
-->
|
||||
<Label fx:id="bitcoinPeersLabel" text="Connected peers:" GridPane.rowIndex="0"/>
|
||||
<TextArea fx:id="bitcoinPeersTextArea" GridPane.rowIndex="0" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS"
|
||||
<Label text="Use tor:" GridPane.rowIndex="0"/>
|
||||
<CheckBox fx:id="useTorCheckBox" GridPane.rowIndex="0" GridPane.columnIndex="1"/>
|
||||
|
||||
<Label fx:id="bitcoinPeersLabel" text="Connected peers:" GridPane.rowIndex="1"/>
|
||||
<TextArea fx:id="bitcoinPeersTextArea" GridPane.rowIndex="1" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS"
|
||||
GridPane.vgrow="SOMETIMES" editable="false" focusTraversable="false"/>
|
||||
|
||||
<TitledGroupBg text="P2P network" GridPane.rowIndex="1" GridPane.rowSpan="5">
|
||||
<TitledGroupBg text="P2P network" GridPane.rowIndex="2" GridPane.rowSpan="5">
|
||||
<padding>
|
||||
<Insets top="50.0"/>
|
||||
</padding>
|
||||
|
@ -50,20 +50,20 @@
|
|||
</GridPane.margin>
|
||||
</TitledGroupBg>
|
||||
|
||||
<Label text="My onion address:" GridPane.rowIndex="1">
|
||||
<Label text="My onion address:" GridPane.rowIndex="2">
|
||||
<GridPane.margin>
|
||||
<Insets top="50.0"/>
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<TextField fx:id="onionAddress" GridPane.rowIndex="1" GridPane.columnIndex="1"
|
||||
<TextField fx:id="onionAddress" GridPane.rowIndex="2" GridPane.columnIndex="1"
|
||||
editable="false" focusTraversable="false">
|
||||
<GridPane.margin>
|
||||
<Insets top="50.0"/>
|
||||
</GridPane.margin>
|
||||
</TextField>
|
||||
|
||||
<Label fx:id="p2PPeersLabel" text="Connected peers:" GridPane.rowIndex="2"/>
|
||||
<TableView fx:id="tableView" GridPane.rowIndex="2" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS"
|
||||
<Label fx:id="p2PPeersLabel" text="Connected peers:" GridPane.rowIndex="3"/>
|
||||
<TableView fx:id="tableView" GridPane.rowIndex="3" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS"
|
||||
GridPane.vgrow="ALWAYS">
|
||||
<columns>
|
||||
<TableColumn text="Onion address" fx:id="onionAddressColumn" minWidth="220">
|
||||
|
@ -109,14 +109,14 @@
|
|||
</columns>
|
||||
</TableView>
|
||||
|
||||
<Label text="Total traffic:" GridPane.rowIndex="3"/>
|
||||
<TextField fx:id="totalTraffic" GridPane.rowIndex="3" GridPane.columnIndex="1" editable="false"
|
||||
<Label text="Total traffic:" GridPane.rowIndex="4"/>
|
||||
<TextField fx:id="totalTraffic" GridPane.rowIndex="4" GridPane.columnIndex="1" editable="false"
|
||||
focusTraversable="false"/>
|
||||
|
||||
<Label text="Use Tor bridges:" GridPane.rowIndex="4"/>
|
||||
<CheckBox fx:id="useBridgesCheckBox" GridPane.rowIndex="4" GridPane.columnIndex="1"/>
|
||||
<Label fx:id="bridgesLabel" text="Tor bridges:" GridPane.rowIndex="5" visible="false" managed="false"/>
|
||||
<TextArea fx:id="bridgesTextArea" GridPane.rowIndex="5" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS"
|
||||
<Label text="Use Tor bridges:" GridPane.rowIndex="5"/>
|
||||
<CheckBox fx:id="useBridgesCheckBox" GridPane.rowIndex="5" GridPane.columnIndex="1"/>
|
||||
<Label fx:id="bridgesLabel" text="Tor bridges:" GridPane.rowIndex="6" visible="false" managed="false"/>
|
||||
<TextArea fx:id="bridgesTextArea" GridPane.rowIndex="6" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS"
|
||||
minHeight="60"
|
||||
GridPane.vgrow="SOMETIMES" editable="true" focusTraversable="true" visible="false" managed="false"/>
|
||||
|
||||
|
|
|
@ -17,11 +17,14 @@
|
|||
|
||||
package io.bitsquare.gui.main.settings.network;
|
||||
|
||||
import io.bitsquare.app.BitsquareApp;
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.common.Clock;
|
||||
import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.gui.common.model.Activatable;
|
||||
import io.bitsquare.gui.common.view.ActivatableViewAndModel;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.main.overlays.popups.Popup;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.p2p.P2PService;
|
||||
import io.bitsquare.p2p.network.Statistic;
|
||||
|
@ -41,6 +44,7 @@ import org.fxmisc.easybind.Subscription;
|
|||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@FxmlView
|
||||
|
@ -61,8 +65,8 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
|
|||
TextArea bitcoinPeersTextArea, bridgesTextArea;
|
||||
@FXML
|
||||
Label bitcoinPeersLabel, p2PPeersLabel, bridgesLabel;
|
||||
/* @FXML
|
||||
CheckBox useTorCheckBox;*/
|
||||
@FXML
|
||||
CheckBox useTorCheckBox;
|
||||
@FXML
|
||||
TableView<P2pNetworkListItem> tableView;
|
||||
@FXML
|
||||
|
@ -124,7 +128,7 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
|
|||
|
||||
@Override
|
||||
public void activate() {
|
||||
/* useTorCheckBox.setSelected(preferences.getUseTorForBitcoinJ());
|
||||
useTorCheckBox.setSelected(preferences.getUseTorForBitcoinJ());
|
||||
useTorCheckBox.setOnAction(event -> {
|
||||
boolean selected = useTorCheckBox.isSelected();
|
||||
if (selected != preferences.getUseTorForBitcoinJ()) {
|
||||
|
@ -139,7 +143,7 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
|
|||
.onClose(() -> useTorCheckBox.setSelected(!selected))
|
||||
.show();
|
||||
}
|
||||
});*/
|
||||
});
|
||||
bitcoinPeersSubscription = EasyBind.subscribe(walletService.connectedPeersProperty(), connectedPeers -> updateBitcoinPeersTextArea());
|
||||
|
||||
nodeAddressSubscription = EasyBind.subscribe(p2PService.getNetworkNode().nodeAddressProperty(),
|
||||
|
@ -166,7 +170,7 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
|
|||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
//useTorCheckBox.setOnAction(null);
|
||||
useTorCheckBox.setOnAction(null);
|
||||
|
||||
if (nodeAddressSubscription != null)
|
||||
nodeAddressSubscription.unsubscribe();
|
||||
|
|
|
@ -443,7 +443,11 @@ public class BSFormatter {
|
|||
duration = StringUtils.replaceOnce(duration, " 1 minutes", " 1 minute");
|
||||
duration = StringUtils.replaceOnce(duration, " 1 hours", " 1 hour");
|
||||
duration = StringUtils.replaceOnce(duration, " 1 days", " 1 day");
|
||||
if (duration.equals(" ,"))
|
||||
if (duration.startsWith(", "))
|
||||
duration = duration.replace(" ,", "");
|
||||
else if (duration.startsWith(", "))
|
||||
duration = duration.replace(", ", "");
|
||||
if (duration.equals(""))
|
||||
duration = "Trade period is over";
|
||||
return duration.trim();
|
||||
}
|
||||
|
|
|
@ -14,9 +14,22 @@
|
|||
<logger name="io.bitsquare.storage.FileManager" level="WARN"/>
|
||||
<logger name="io.bitsquare.locale.BSResources" level="ERROR"/>
|
||||
|
||||
<logger name="io.bitsquare.p2p" level="WARN"/>
|
||||
<!--<logger name="io.bitsquare.p2p" level="WARN"/>-->
|
||||
<logger name="io.bitsquare.btc.pricefeed" level="WARN"/>
|
||||
|
||||
|
||||
<logger name="io.bitsquare.storage.Storage" level="WARN"/>
|
||||
<logger name="io.bitsquare.storage.FileManager" level="WARN"/>
|
||||
|
||||
<logger name="io.bitsquare.p2p.peers" level="WARN"/>
|
||||
<logger name="io.bitsquare.p2p.peers.getdata" level="WARN"/>
|
||||
<logger name="io.bitsquare.p2p.peers.keepalive" level="WARN"/>
|
||||
<logger name="io.bitsquare.p2p.peers.peerexchange" level="WARN"/>
|
||||
|
||||
<!--<logger name="io.bitsquare.p2p.network" level="WARN"/>-->
|
||||
<!-- <logger name="io.bitsquare.p2p.P2PService" level="WARN"/>-->
|
||||
|
||||
|
||||
<!-- <logger name="io.bitsquare.p2p.peers.PeerGroup" level="TRACE"/>
|
||||
|
||||
|
||||
|
@ -47,10 +60,13 @@
|
|||
<logger name="com.msopentech.thali.toronionproxy.OnionProxyManagerEventHandler" level="INFO"/>
|
||||
|
||||
<logger name="org.bitcoinj" level="WARN"/>
|
||||
<logger name="org.bitcoinj.core.Peer" level="ERROR"/>-->
|
||||
<logger name="org.bitcoinj.core.PeerGroup" level="ERROR"/>-->
|
||||
<logger name="org.bitcoinj.net.ConnectionHandler" level="ERROR"/>-->
|
||||
<logger name="org.bitcoinj.core.PeerSocketHandler" level="ERROR"/>-->
|
||||
|
||||
<logger name="org.bitcoinj.net.NioClientManager" level="ERROR"/>-->
|
||||
<logger name="org.bitcoinj.net.MultiplexingDiscovery" level="ERROR"/>-->
|
||||
|
||||
<!-- <logger name="org.bitcoinj.core.Peer" level="TRACE"/>-->
|
||||
|
||||
</configuration>
|
||||
|
|
85
headless/pom.xml
Normal file
85
headless/pom.xml
Normal file
|
@ -0,0 +1,85 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>parent</artifactId>
|
||||
<groupId>io.bitsquare</groupId>
|
||||
<version>0.4.9</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>headless</artifactId>
|
||||
|
||||
<build>
|
||||
|
||||
<resources>
|
||||
<resource>
|
||||
<filtering>false</filtering>
|
||||
<directory>${basedir}/src/main/java</directory>
|
||||
<includes>
|
||||
<include>**/*.fxml</include>
|
||||
<include>**/*.css</include>
|
||||
</includes>
|
||||
</resource>
|
||||
<resource>
|
||||
<filtering>false</filtering>
|
||||
<directory>${basedir}/src/main/resources</directory>
|
||||
<includes>
|
||||
<include>**/*.*</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.3</version>
|
||||
<configuration>
|
||||
<!-- broken with Java 8 (MSHADE-174), using ProGuard instead. -->
|
||||
<minimizeJar>false</minimizeJar>
|
||||
<transformers>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>io.bitsquare.headless.HeadlessMain</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
<filters>
|
||||
<filter>
|
||||
<!-- exclude signatures, the bundling process breaks them for some reason -->
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<shadedArtifactAttached>true</shadedArtifactAttached>
|
||||
<shadedClassifierName>bundled</shadedClassifierName>
|
||||
<finalName>Headless</finalName>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.bitsquare</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
160
headless/src/main/java/io/bitsquare/headless/Headless.java
Normal file
160
headless/src/main/java/io/bitsquare/headless/Headless.java
Normal file
|
@ -0,0 +1,160 @@
|
|||
package io.bitsquare.headless;
|
||||
|
||||
import ch.qos.logback.classic.Level;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import io.bitsquare.app.BitsquareEnvironment;
|
||||
import io.bitsquare.app.Log;
|
||||
import io.bitsquare.app.Version;
|
||||
import io.bitsquare.arbitration.ArbitratorManager;
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.common.CommonOptionKeys;
|
||||
import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
import io.bitsquare.common.util.Utilities;
|
||||
import io.bitsquare.p2p.P2PService;
|
||||
import io.bitsquare.p2p.P2PServiceListener;
|
||||
import io.bitsquare.trade.offer.OfferBookService;
|
||||
import io.bitsquare.trade.offer.OpenOfferManager;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.bitcoinj.store.BlockStoreException;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
import java.security.Security;
|
||||
|
||||
public class Headless {
|
||||
private static final Logger log = LoggerFactory.getLogger(Headless.class);
|
||||
private static Environment env;
|
||||
private final Injector injector;
|
||||
private final OfferBookService offerBookService;
|
||||
private final OpenOfferManager openOfferManager;
|
||||
private final HeadlessModule headlessModule;
|
||||
|
||||
private P2PService p2pService;
|
||||
|
||||
public static void setEnvironment(Environment env) {
|
||||
Headless.env = env;
|
||||
}
|
||||
|
||||
public Headless() {
|
||||
String logPath = Paths.get(env.getProperty(BitsquareEnvironment.APP_DATA_DIR_KEY), "bitsquare").toString();
|
||||
Log.setup(logPath);
|
||||
log.info("Log files under: " + logPath);
|
||||
Version.printVersion();
|
||||
Utilities.printSysInfo();
|
||||
Log.setLevel(Level.toLevel(env.getRequiredProperty(CommonOptionKeys.LOG_LEVEL_KEY)));
|
||||
|
||||
// setup UncaughtExceptionHandler
|
||||
Thread.UncaughtExceptionHandler handler = (thread, throwable) -> {
|
||||
// Might come from another thread
|
||||
if (throwable.getCause() != null && throwable.getCause().getCause() != null &&
|
||||
throwable.getCause().getCause() instanceof BlockStoreException) {
|
||||
log.error(throwable.getMessage());
|
||||
} else {
|
||||
log.error("Uncaught Exception from thread " + Thread.currentThread().getName());
|
||||
log.error("throwableMessage= " + throwable.getMessage());
|
||||
log.error("throwableClass= " + throwable.getClass());
|
||||
log.error("Stack trace:\n" + ExceptionUtils.getStackTrace(throwable));
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
};
|
||||
Thread.setDefaultUncaughtExceptionHandler(handler);
|
||||
Thread.currentThread().setUncaughtExceptionHandler(handler);
|
||||
|
||||
if (Utilities.isRestrictedCryptography())
|
||||
Utilities.removeCryptographyRestrictions();
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
|
||||
|
||||
headlessModule = new HeadlessModule(env);
|
||||
injector = Guice.createInjector(headlessModule);
|
||||
Version.setBtcNetworkId(injector.getInstance(BitsquareEnvironment.class).getBitcoinNetwork().ordinal());
|
||||
p2pService = injector.getInstance(P2PService.class);
|
||||
offerBookService = injector.getInstance(OfferBookService.class);
|
||||
openOfferManager = injector.getInstance(OpenOfferManager.class);
|
||||
p2pService.start(false, new P2PServiceListener() {
|
||||
@Override
|
||||
public void onRequestingDataCompleted() {
|
||||
openOfferManager.onAllServicesInitialized();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNoSeedNodeAvailable() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNoPeersAvailable() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBootstrapComplete() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTorNodeReady() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHiddenServicePublished() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetupFailed(Throwable throwable) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUseDefaultBridges() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestCustomBridges(Runnable resultHandler) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void shutDown() {
|
||||
gracefulShutDown(() -> {
|
||||
log.info("Shutdown complete");
|
||||
System.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
private void gracefulShutDown(ResultHandler resultHandler) {
|
||||
log.debug("gracefulShutDown");
|
||||
try {
|
||||
if (injector != null) {
|
||||
injector.getInstance(ArbitratorManager.class).shutDown();
|
||||
injector.getInstance(OpenOfferManager.class).shutDown(() -> {
|
||||
injector.getInstance(P2PService.class).shutDown(() -> {
|
||||
injector.getInstance(WalletService.class).shutDownDone.addListener((ov, o, n) -> {
|
||||
headlessModule.close(injector);
|
||||
log.info("Graceful shutdown completed");
|
||||
resultHandler.handleResult();
|
||||
});
|
||||
injector.getInstance(WalletService.class).shutDown();
|
||||
});
|
||||
});
|
||||
// we wait max 5 sec.
|
||||
UserThread.runAfter(resultHandler::handleResult, 5);
|
||||
} else {
|
||||
UserThread.runAfter(resultHandler::handleResult, 1);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
log.info("App shutdown failed with exception");
|
||||
t.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.headless;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import io.bitsquare.app.BitsquareEnvironment;
|
||||
import io.bitsquare.app.BitsquareExecutable;
|
||||
import io.bitsquare.common.UserThread;
|
||||
import joptsimple.OptionException;
|
||||
import joptsimple.OptionParser;
|
||||
import joptsimple.OptionSet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Scanner;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import static io.bitsquare.app.BitsquareEnvironment.*;
|
||||
|
||||
public class HeadlessMain extends BitsquareExecutable {
|
||||
private static final Logger log = LoggerFactory.getLogger(HeadlessMain.class);
|
||||
private Headless headless;
|
||||
private boolean isStopped;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
|
||||
.setNameFormat("HeadlessMain")
|
||||
.setDaemon(true)
|
||||
.build();
|
||||
UserThread.setExecutor(Executors.newSingleThreadExecutor(threadFactory));
|
||||
|
||||
// We don't want to do the full argument parsing here as that might easily change in update versions
|
||||
// So we only handle the absolute minimum which is APP_NAME, APP_DATA_DIR_KEY and USER_DATA_DIR
|
||||
BitsquareEnvironment.setDefaultAppName("Bitsquare_headless");
|
||||
OptionParser parser = new OptionParser();
|
||||
parser.allowsUnrecognizedOptions();
|
||||
parser.accepts(USER_DATA_DIR_KEY, description("User data directory", DEFAULT_USER_DATA_DIR))
|
||||
.withRequiredArg();
|
||||
parser.accepts(APP_NAME_KEY, description("Application name", DEFAULT_APP_NAME))
|
||||
.withRequiredArg();
|
||||
|
||||
OptionSet options;
|
||||
try {
|
||||
options = parser.parse(args);
|
||||
} catch (OptionException ex) {
|
||||
System.out.println("error: " + ex.getMessage());
|
||||
System.out.println();
|
||||
parser.printHelpOn(System.out);
|
||||
System.exit(EXIT_FAILURE);
|
||||
return;
|
||||
}
|
||||
BitsquareEnvironment bitsquareEnvironment = new BitsquareEnvironment(options);
|
||||
|
||||
// need to call that before BitsquareAppMain().execute(args)
|
||||
BitsquareExecutable.initAppDir(bitsquareEnvironment.getProperty(BitsquareEnvironment.APP_DATA_DIR_KEY));
|
||||
|
||||
// For some reason the JavaFX launch process results in us losing the thread context class loader: reset it.
|
||||
// In order to work around a bug in JavaFX 8u25 and below, you must include the following code as the first line of your realMain method:
|
||||
Thread.currentThread().setContextClassLoader(HeadlessMain.class.getClassLoader());
|
||||
|
||||
new HeadlessMain().execute(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(OptionSet options) {
|
||||
Headless.setEnvironment(new BitsquareEnvironment(options));
|
||||
UserThread.execute(() -> headless = new Headless());
|
||||
|
||||
while (!isStopped) {
|
||||
try {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
while (scanner.hasNextLine()) {
|
||||
String inputString = scanner.nextLine();
|
||||
if (inputString.equals("q")) {
|
||||
UserThread.execute(headless::shutDown);
|
||||
isStopped = true;
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
110
headless/src/main/java/io/bitsquare/headless/HeadlessModule.java
Normal file
110
headless/src/main/java/io/bitsquare/headless/HeadlessModule.java
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.headless;
|
||||
|
||||
import com.google.inject.Singleton;
|
||||
import io.bitsquare.alert.AlertModule;
|
||||
import io.bitsquare.app.AppModule;
|
||||
import io.bitsquare.app.BitsquareEnvironment;
|
||||
import io.bitsquare.arbitration.ArbitratorModule;
|
||||
import io.bitsquare.btc.BitcoinModule;
|
||||
import io.bitsquare.common.Clock;
|
||||
import io.bitsquare.common.crypto.KeyRing;
|
||||
import io.bitsquare.common.crypto.KeyStorage;
|
||||
import io.bitsquare.crypto.EncryptionServiceModule;
|
||||
import io.bitsquare.filter.FilterModule;
|
||||
import io.bitsquare.p2p.P2PModule;
|
||||
import io.bitsquare.storage.Storage;
|
||||
import io.bitsquare.trade.TradeModule;
|
||||
import io.bitsquare.trade.offer.OfferModule;
|
||||
import io.bitsquare.user.Preferences;
|
||||
import io.bitsquare.user.User;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static com.google.inject.name.Names.named;
|
||||
|
||||
class HeadlessModule extends AppModule {
|
||||
private static final Logger log = LoggerFactory.getLogger(HeadlessModule.class);
|
||||
|
||||
public HeadlessModule(Environment env) {
|
||||
super(env);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(KeyStorage.class).in(Singleton.class);
|
||||
bind(KeyRing.class).in(Singleton.class);
|
||||
bind(User.class).in(Singleton.class);
|
||||
bind(Preferences.class).in(Singleton.class);
|
||||
bind(Clock.class).in(Singleton.class);
|
||||
|
||||
File storageDir = new File(env.getRequiredProperty(Storage.DIR_KEY));
|
||||
bind(File.class).annotatedWith(named(Storage.DIR_KEY)).toInstance(storageDir);
|
||||
|
||||
File keyStorageDir = new File(env.getRequiredProperty(KeyStorage.DIR_KEY));
|
||||
bind(File.class).annotatedWith(named(KeyStorage.DIR_KEY)).toInstance(keyStorageDir);
|
||||
|
||||
bind(BitsquareEnvironment.class).toInstance((BitsquareEnvironment) env);
|
||||
|
||||
// ordering is used for shut down sequence
|
||||
install(tradeModule());
|
||||
install(encryptionServiceModule());
|
||||
install(arbitratorModule());
|
||||
install(offerModule());
|
||||
install(torModule());
|
||||
install(bitcoinModule());
|
||||
install(alertModule());
|
||||
install(filterModule());
|
||||
}
|
||||
|
||||
private TradeModule tradeModule() {
|
||||
return new TradeModule(env);
|
||||
}
|
||||
|
||||
private EncryptionServiceModule encryptionServiceModule() {
|
||||
return new EncryptionServiceModule(env);
|
||||
}
|
||||
|
||||
private ArbitratorModule arbitratorModule() {
|
||||
return new ArbitratorModule(env);
|
||||
}
|
||||
|
||||
private AlertModule alertModule() {
|
||||
return new AlertModule(env);
|
||||
}
|
||||
|
||||
private FilterModule filterModule() {
|
||||
return new FilterModule(env);
|
||||
}
|
||||
|
||||
private OfferModule offerModule() {
|
||||
return new OfferModule(env);
|
||||
}
|
||||
|
||||
private P2PModule torModule() {
|
||||
return new P2PModule(env);
|
||||
}
|
||||
|
||||
private BitcoinModule bitcoinModule() {
|
||||
return new BitcoinModule(env);
|
||||
}
|
||||
}
|
42
headless/src/main/resources/logback.xml
Normal file
42
headless/src/main/resources/logback.xml
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ This file is part of Bitsquare.
|
||||
~
|
||||
~ Bitsquare is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU Affero General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or (at
|
||||
~ your option) any later version.
|
||||
~
|
||||
~ Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
~ License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU Affero General Public License
|
||||
~ along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<configuration>
|
||||
<appender name="CONSOLE_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%highlight(%d{MMM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{15}: %msg %xEx%n)</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="TRACE">
|
||||
<appender-ref ref="CONSOLE_APPENDER"/>
|
||||
</root>
|
||||
|
||||
<logger name="io.bitsquare.storage.Storage" level="WARN"/>
|
||||
<logger name="io.bitsquare.storage.FileManager" level="WARN"/>
|
||||
|
||||
<!-- <logger name="io.bitsquare.p2p.peers.PeerGroup" level="INFO"/>
|
||||
<logger name="io.bitsquare.p2p.P2PService" level="INFO"/>
|
||||
<logger name="io.bitsquare.p2p.storage.ProtectedExpirableDataStorage" level="INFO"/>
|
||||
<logger name="io.bitsquare.p2p.network.LocalhostNetworkNode" level="INFO"/>
|
||||
<logger name="io.bitsquare.p2p.network.TorNetworkNode" level="TRACE"/>
|
||||
<logger name="io.bitsquare.p2p.network.NetworkNode" level="TRACE"/>-->
|
||||
|
||||
<logger name="com.msopentech.thali.toronionproxy.OnionProxyManagerEventHandler" level="INFO"/>
|
||||
|
||||
</configuration>
|
|
@ -1,384 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.java2d.xr;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.geom.*;
|
||||
import sun.awt.SunToolkit;
|
||||
import sun.java2d.InvalidPipeException;
|
||||
import sun.java2d.SunGraphics2D;
|
||||
import sun.java2d.loops.*;
|
||||
import sun.java2d.pipe.Region;
|
||||
import sun.java2d.pipe.PixelDrawPipe;
|
||||
import sun.java2d.pipe.PixelFillPipe;
|
||||
import sun.java2d.pipe.ShapeDrawPipe;
|
||||
import sun.java2d.pipe.SpanIterator;
|
||||
import sun.java2d.pipe.ShapeSpanIterator;
|
||||
import sun.java2d.pipe.LoopPipe;
|
||||
|
||||
import static sun.java2d.xr.XRUtils.clampToShort;
|
||||
import static sun.java2d.xr.XRUtils.clampToUShort;
|
||||
|
||||
/**
|
||||
* XRender provides only accalerated rectangles. To emulate higher "order"
|
||||
* geometry we have to pass everything else to DoPath/FillSpans.
|
||||
*
|
||||
* TODO: DrawRect could be instrified
|
||||
*
|
||||
* @author Clemens Eisserer
|
||||
*/
|
||||
|
||||
public class XRRenderer implements PixelDrawPipe, PixelFillPipe, ShapeDrawPipe {
|
||||
XRDrawHandler drawHandler;
|
||||
MaskTileManager tileManager;
|
||||
XRDrawLine lineGen;
|
||||
GrowableRectArray rectBuffer;
|
||||
|
||||
public XRRenderer(MaskTileManager tileManager) {
|
||||
this.tileManager = tileManager;
|
||||
this.rectBuffer = tileManager.getMainTile().getRects();
|
||||
|
||||
this.drawHandler = new XRDrawHandler();
|
||||
this.lineGen = new XRDrawLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Common validate method, used by all XRRender functions to validate the
|
||||
* destination context.
|
||||
*/
|
||||
private final void validateSurface(SunGraphics2D sg2d) {
|
||||
XRSurfaceData xrsd;
|
||||
try {
|
||||
xrsd = (XRSurfaceData) sg2d.surfaceData;
|
||||
} catch (ClassCastException e) {
|
||||
throw new InvalidPipeException("wrong surface data type: " + sg2d.surfaceData);
|
||||
}
|
||||
xrsd.validateAsDestination(sg2d, sg2d.getCompClip());
|
||||
xrsd.maskBuffer.validateCompositeState(sg2d.composite, sg2d.transform,
|
||||
sg2d.paint, sg2d);
|
||||
}
|
||||
|
||||
public void drawLine(SunGraphics2D sg2d, int x1, int y1, int x2, int y2) {
|
||||
Region compClip = sg2d.getCompClip();
|
||||
int transX1 = Region.clipAdd(x1, sg2d.transX);
|
||||
int transY1 = Region.clipAdd(y1, sg2d.transY);
|
||||
int transX2 = Region.clipAdd(x2, sg2d.transX);
|
||||
int transY2 = Region.clipAdd(y2, sg2d.transY);
|
||||
|
||||
SunToolkit.awtLock();
|
||||
try {
|
||||
validateSurface(sg2d);
|
||||
lineGen.rasterizeLine(rectBuffer, transX1, transY1,
|
||||
transX2, transY2, compClip.getLoX(), compClip.getLoY(),
|
||||
compClip.getHiX(), compClip.getHiY(), true, true);
|
||||
tileManager.fillMask((XRSurfaceData) sg2d.surfaceData);
|
||||
} finally {
|
||||
SunToolkit.awtUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void drawRect(SunGraphics2D sg2d,
|
||||
int x, int y, int width, int height) {
|
||||
draw(sg2d, new Rectangle2D.Float(x, y, width, height));
|
||||
}
|
||||
|
||||
public void drawPolyline(SunGraphics2D sg2d,
|
||||
int xpoints[], int ypoints[], int npoints) {
|
||||
Path2D.Float p2d = new Path2D.Float();
|
||||
if (npoints > 1) {
|
||||
p2d.moveTo(xpoints[0], ypoints[0]);
|
||||
for (int i = 1; i < npoints; i++) {
|
||||
p2d.lineTo(xpoints[i], ypoints[i]);
|
||||
}
|
||||
}
|
||||
|
||||
draw(sg2d, p2d);
|
||||
}
|
||||
|
||||
public void drawPolygon(SunGraphics2D sg2d,
|
||||
int xpoints[], int ypoints[], int npoints) {
|
||||
draw(sg2d, new Polygon(xpoints, ypoints, npoints));
|
||||
}
|
||||
|
||||
public void fillRect(SunGraphics2D sg2d, int x, int y, int width, int height) {
|
||||
x = Region.clipAdd(x, sg2d.transX);
|
||||
y = Region.clipAdd(y, sg2d.transY);
|
||||
|
||||
/*
|
||||
* Limit x/y to signed short, width/height to unsigned short,
|
||||
* to match the X11 coordinate limits for rectangles.
|
||||
* Correct width/height in case x/y have been modified by clipping.
|
||||
*/
|
||||
if (x > Short.MAX_VALUE || y > Short.MAX_VALUE) {
|
||||
return;
|
||||
}
|
||||
|
||||
int x2 = Region.dimAdd(x, width);
|
||||
int y2 = Region.dimAdd(y, height);
|
||||
|
||||
if (x2 < Short.MIN_VALUE || y2 < Short.MIN_VALUE) {
|
||||
return;
|
||||
}
|
||||
|
||||
x = clampToShort(x);
|
||||
y = clampToShort(y);
|
||||
width = clampToUShort(x2 - x);
|
||||
height = clampToUShort(y2 - y);
|
||||
|
||||
if (width == 0 || height == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
SunToolkit.awtLock();
|
||||
try {
|
||||
validateSurface(sg2d);
|
||||
rectBuffer.pushRectValues(x, y, width, height);
|
||||
tileManager.fillMask((XRSurfaceData) sg2d.surfaceData);
|
||||
} finally {
|
||||
SunToolkit.awtUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void fillPolygon(SunGraphics2D sg2d,
|
||||
int xpoints[], int ypoints[], int npoints) {
|
||||
fill(sg2d, new Polygon(xpoints, ypoints, npoints));
|
||||
}
|
||||
|
||||
public void drawRoundRect(SunGraphics2D sg2d,
|
||||
int x, int y, int width, int height,
|
||||
int arcWidth, int arcHeight) {
|
||||
draw(sg2d, new RoundRectangle2D.Float(x, y, width, height,
|
||||
arcWidth, arcHeight));
|
||||
}
|
||||
|
||||
public void fillRoundRect(SunGraphics2D sg2d, int x, int y,
|
||||
int width, int height,
|
||||
int arcWidth, int arcHeight) {
|
||||
fill(sg2d, new RoundRectangle2D.Float(x, y, width, height,
|
||||
arcWidth, arcHeight));
|
||||
}
|
||||
|
||||
public void drawOval(SunGraphics2D sg2d,
|
||||
int x, int y, int width, int height) {
|
||||
draw(sg2d, new Ellipse2D.Float(x, y, width, height));
|
||||
}
|
||||
|
||||
public void fillOval(SunGraphics2D sg2d,
|
||||
int x, int y, int width, int height) {
|
||||
fill(sg2d, new Ellipse2D.Float(x, y, width, height));
|
||||
}
|
||||
|
||||
public void drawArc(SunGraphics2D sg2d,
|
||||
int x, int y, int width, int height,
|
||||
int startAngle, int arcAngle) {
|
||||
draw(sg2d, new Arc2D.Float(x, y, width, height,
|
||||
startAngle, arcAngle, Arc2D.OPEN));
|
||||
}
|
||||
|
||||
public void fillArc(SunGraphics2D sg2d,
|
||||
int x, int y, int width, int height,
|
||||
int startAngle, int arcAngle) {
|
||||
fill(sg2d, new Arc2D.Float(x, y, width, height,
|
||||
startAngle, arcAngle, Arc2D.PIE));
|
||||
}
|
||||
|
||||
private class XRDrawHandler extends ProcessPath.DrawHandler {
|
||||
DirtyRegion region;
|
||||
|
||||
XRDrawHandler() {
|
||||
// these are bogus values; the caller will use validate()
|
||||
// to ensure that they are set properly prior to each usage
|
||||
super(0, 0, 0, 0);
|
||||
this.region = new DirtyRegion();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method needs to be called prior to each draw/fillPath()
|
||||
* operation to ensure the clip bounds are up to date.
|
||||
*/
|
||||
void validate(SunGraphics2D sg2d) {
|
||||
Region clip = sg2d.getCompClip();
|
||||
setBounds(clip.getLoX(), clip.getLoY(),
|
||||
clip.getHiX(), clip.getHiY(), sg2d.strokeHint);
|
||||
validateSurface(sg2d);
|
||||
}
|
||||
|
||||
public void drawLine(int x1, int y1, int x2, int y2) {
|
||||
region.setDirtyLineRegion(x1, y1, x2, y2);
|
||||
int xDiff = region.x2 - region.x;
|
||||
int yDiff = region.y2 - region.y;
|
||||
|
||||
if (xDiff == 0 || yDiff == 0) {
|
||||
// horizontal / diagonal lines can be represented by a single
|
||||
// rectangle
|
||||
rectBuffer.pushRectValues(region.x, region.y, region.x2 - region.x
|
||||
+ 1, region.y2 - region.y + 1);
|
||||
} else if (xDiff == 1 && yDiff == 1) {
|
||||
// fast path for pattern commonly generated by
|
||||
// ProcessPath.DrawHandler
|
||||
rectBuffer.pushRectValues(x1, y1, 1, 1);
|
||||
rectBuffer.pushRectValues(x2, y2, 1, 1);
|
||||
} else {
|
||||
lineGen.rasterizeLine(rectBuffer, x1, y1, x2, y2, 0, 0,
|
||||
0, 0, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void drawPixel(int x, int y) {
|
||||
rectBuffer.pushRectValues(x, y, 1, 1);
|
||||
}
|
||||
|
||||
public void drawScanline(int x1, int x2, int y) {
|
||||
rectBuffer.pushRectValues(x1, y, x2 - x1 + 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
protected void drawPath(SunGraphics2D sg2d, Path2D.Float p2df,
|
||||
int transx, int transy) {
|
||||
SunToolkit.awtLock();
|
||||
try {
|
||||
validateSurface(sg2d);
|
||||
drawHandler.validate(sg2d);
|
||||
ProcessPath.drawPath(drawHandler, p2df, transx, transy);
|
||||
tileManager.fillMask(((XRSurfaceData) sg2d.surfaceData));
|
||||
} finally {
|
||||
SunToolkit.awtUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
protected void fillPath(SunGraphics2D sg2d, Path2D.Float p2df,
|
||||
int transx, int transy) {
|
||||
SunToolkit.awtLock();
|
||||
try {
|
||||
validateSurface(sg2d);
|
||||
drawHandler.validate(sg2d);
|
||||
ProcessPath.fillPath(drawHandler, p2df, transx, transy);
|
||||
tileManager.fillMask(((XRSurfaceData) sg2d.surfaceData));
|
||||
} finally {
|
||||
SunToolkit.awtUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
protected void fillSpans(SunGraphics2D sg2d, SpanIterator si,
|
||||
int transx, int transy) {
|
||||
SunToolkit.awtLock();
|
||||
try {
|
||||
validateSurface(sg2d);
|
||||
int[] spanBox = new int[4];
|
||||
while (si.nextSpan(spanBox)) {
|
||||
rectBuffer.pushRectValues(spanBox[0] + transx,
|
||||
spanBox[1] + transy,
|
||||
spanBox[2] - spanBox[0],
|
||||
spanBox[3] - spanBox[1]);
|
||||
}
|
||||
tileManager.fillMask(((XRSurfaceData) sg2d.surfaceData));
|
||||
} finally {
|
||||
SunToolkit.awtUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void draw(SunGraphics2D sg2d, Shape s) {
|
||||
if (sg2d.strokeState == SunGraphics2D.STROKE_THIN) {
|
||||
Path2D.Float p2df;
|
||||
int transx, transy;
|
||||
if (sg2d.transformState <= SunGraphics2D.TRANSFORM_INT_TRANSLATE) {
|
||||
if (s instanceof Path2D.Float) {
|
||||
p2df = (Path2D.Float) s;
|
||||
} else {
|
||||
p2df = new Path2D.Float(s);
|
||||
}
|
||||
transx = sg2d.transX;
|
||||
transy = sg2d.transY;
|
||||
} else {
|
||||
p2df = new Path2D.Float(s, sg2d.transform);
|
||||
transx = 0;
|
||||
transy = 0;
|
||||
}
|
||||
drawPath(sg2d, p2df, transx, transy);
|
||||
} else if (sg2d.strokeState < SunGraphics2D.STROKE_CUSTOM) {
|
||||
ShapeSpanIterator si = LoopPipe.getStrokeSpans(sg2d, s);
|
||||
try {
|
||||
fillSpans(sg2d, si, 0, 0);
|
||||
} finally {
|
||||
si.dispose();
|
||||
}
|
||||
} else {
|
||||
fill(sg2d, sg2d.stroke.createStrokedShape(s));
|
||||
}
|
||||
}
|
||||
|
||||
public void fill(SunGraphics2D sg2d, Shape s) {
|
||||
int transx, transy;
|
||||
|
||||
if (sg2d.strokeState == SunGraphics2D.STROKE_THIN) {
|
||||
// Here we are able to use fillPath() for
|
||||
// high-quality fills.
|
||||
Path2D.Float p2df;
|
||||
if (sg2d.transformState <= SunGraphics2D.TRANSFORM_INT_TRANSLATE) {
|
||||
if (s instanceof Path2D.Float) {
|
||||
p2df = (Path2D.Float) s;
|
||||
} else {
|
||||
p2df = new Path2D.Float(s);
|
||||
}
|
||||
transx = sg2d.transX;
|
||||
transy = sg2d.transY;
|
||||
} else {
|
||||
p2df = new Path2D.Float(s, sg2d.transform);
|
||||
transx = 0;
|
||||
transy = 0;
|
||||
}
|
||||
fillPath(sg2d, p2df, transx, transy);
|
||||
return;
|
||||
}
|
||||
|
||||
AffineTransform at;
|
||||
if (sg2d.transformState <= SunGraphics2D.TRANSFORM_INT_TRANSLATE) {
|
||||
// Transform (translation) will be done by FillSpans
|
||||
at = null;
|
||||
transx = sg2d.transX;
|
||||
transy = sg2d.transY;
|
||||
} else {
|
||||
// Transform will be done by the PathIterator
|
||||
at = sg2d.transform;
|
||||
transx = transy = 0;
|
||||
}
|
||||
|
||||
ShapeSpanIterator ssi = LoopPipe.getFillSSI(sg2d);
|
||||
try {
|
||||
// Subtract transx/y from the SSI clip to match the
|
||||
// (potentially untranslated) geometry fed to it
|
||||
Region clip = sg2d.getCompClip();
|
||||
ssi.setOutputAreaXYXY(clip.getLoX() - transx,
|
||||
clip.getLoY() - transy,
|
||||
clip.getHiX() - transx,
|
||||
clip.getHiY() - transy);
|
||||
ssi.appendPath(s.getPathIterator(at));
|
||||
fillSpans(sg2d, ssi, transx, transy);
|
||||
} finally {
|
||||
ssi.dispose();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,6 +33,10 @@ public abstract class TorNode<M extends OnionProxyManager, C extends OnionProxyC
|
|||
log.info("TorSocks running on port " + proxyPort);
|
||||
this.proxy = setupSocksProxy(proxyPort);
|
||||
}
|
||||
|
||||
public Socks5Proxy getSocksProxy() {
|
||||
return proxy;
|
||||
}
|
||||
|
||||
private Socks5Proxy setupSocksProxy(int proxyPort) throws UnknownHostException {
|
||||
Socks5Proxy proxy = new Socks5Proxy(PROXY_LOCALHOST, proxyPort);
|
||||
|
|
|
@ -4,5 +4,5 @@ DisableNetwork 1
|
|||
AvoidDiskWrites 1
|
||||
PidFile pid
|
||||
RunAsDaemon 1
|
||||
SafeSocks 1
|
||||
SafeSocks 0
|
||||
SOCKSPort auto
|
||||
|
|
Binary file not shown.
93
monitor/pom.xml
Normal file
93
monitor/pom.xml
Normal file
|
@ -0,0 +1,93 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>parent</artifactId>
|
||||
<groupId>io.bitsquare</groupId>
|
||||
<version>0.4.9</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>monitor</artifactId>
|
||||
|
||||
<build>
|
||||
|
||||
<resources>
|
||||
<resource>
|
||||
<filtering>false</filtering>
|
||||
<directory>${basedir}/src/main/java</directory>
|
||||
<includes>
|
||||
<include>**/*.fxml</include>
|
||||
<include>**/*.css</include>
|
||||
</includes>
|
||||
</resource>
|
||||
<resource>
|
||||
<filtering>false</filtering>
|
||||
<directory>${basedir}/src/main/resources</directory>
|
||||
<includes>
|
||||
<include>**/*.*</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.3</version>
|
||||
<configuration>
|
||||
<!-- broken with Java 8 (MSHADE-174), using ProGuard instead. -->
|
||||
<minimizeJar>false</minimizeJar>
|
||||
<transformers>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>io.bitsquare.monitor.MonitorMain</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
<filters>
|
||||
<filter>
|
||||
<!-- exclude signatures, the bundling process breaks them for some reason -->
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<shadedArtifactAttached>true</shadedArtifactAttached>
|
||||
<shadedClassifierName>bundled</shadedClassifierName>
|
||||
<finalName>Monitor</finalName>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.bitsquare</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!--py4j-->
|
||||
<!-- http://mvnrepository.com/artifact/net.sf.py4j/py4j -->
|
||||
<dependency>
|
||||
<groupId>net.sf.py4j</groupId>
|
||||
<artifactId>py4j</artifactId>
|
||||
<version>0.10.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
26
monitor/src/main/java/io/bitsquare/monitor/Gateway.java
Normal file
26
monitor/src/main/java/io/bitsquare/monitor/Gateway.java
Normal file
|
@ -0,0 +1,26 @@
|
|||
package io.bitsquare.monitor;
|
||||
|
||||
import io.bitsquare.trade.offer.Offer;
|
||||
import io.bitsquare.trade.offer.OfferBookService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import py4j.GatewayServer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Gateway {
|
||||
private static final Logger log = LoggerFactory.getLogger(Gateway.class);
|
||||
private OfferBookService offerBookService;
|
||||
|
||||
public Gateway(OfferBookService offerBookService) {
|
||||
this.offerBookService = offerBookService;
|
||||
|
||||
GatewayServer gatewayServer = new GatewayServer(this);
|
||||
gatewayServer.start();
|
||||
log.info("Gateway Server Started");
|
||||
}
|
||||
|
||||
public List<Offer> getOffers() {
|
||||
return offerBookService.getOffers();
|
||||
}
|
||||
}
|
163
monitor/src/main/java/io/bitsquare/monitor/Monitor.java
Normal file
163
monitor/src/main/java/io/bitsquare/monitor/Monitor.java
Normal file
|
@ -0,0 +1,163 @@
|
|||
package io.bitsquare.monitor;
|
||||
|
||||
import ch.qos.logback.classic.Level;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import io.bitsquare.app.BitsquareEnvironment;
|
||||
import io.bitsquare.app.Log;
|
||||
import io.bitsquare.app.Version;
|
||||
import io.bitsquare.arbitration.ArbitratorManager;
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.common.CommonOptionKeys;
|
||||
import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
import io.bitsquare.common.util.Utilities;
|
||||
import io.bitsquare.p2p.P2PService;
|
||||
import io.bitsquare.p2p.P2PServiceListener;
|
||||
import io.bitsquare.trade.offer.OfferBookService;
|
||||
import io.bitsquare.trade.offer.OpenOfferManager;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.bitcoinj.store.BlockStoreException;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
import java.security.Security;
|
||||
|
||||
public class Monitor {
|
||||
private static final Logger log = LoggerFactory.getLogger(Monitor.class);
|
||||
private static Environment env;
|
||||
private final Injector injector;
|
||||
private final OfferBookService offerBookService;
|
||||
private final Gateway gateway;
|
||||
private final OpenOfferManager openOfferManager;
|
||||
private final MonitorModule monitorModule;
|
||||
|
||||
private P2PService p2pService;
|
||||
|
||||
public static void setEnvironment(Environment env) {
|
||||
Monitor.env = env;
|
||||
}
|
||||
|
||||
public Monitor() {
|
||||
String logPath = Paths.get(env.getProperty(BitsquareEnvironment.APP_DATA_DIR_KEY), "bitsquare").toString();
|
||||
Log.setup(logPath);
|
||||
log.info("Log files under: " + logPath);
|
||||
Version.printVersion();
|
||||
Utilities.printSysInfo();
|
||||
Log.setLevel(Level.toLevel(env.getRequiredProperty(CommonOptionKeys.LOG_LEVEL_KEY)));
|
||||
|
||||
// setup UncaughtExceptionHandler
|
||||
Thread.UncaughtExceptionHandler handler = (thread, throwable) -> {
|
||||
// Might come from another thread
|
||||
if (throwable.getCause() != null && throwable.getCause().getCause() != null &&
|
||||
throwable.getCause().getCause() instanceof BlockStoreException) {
|
||||
log.error(throwable.getMessage());
|
||||
} else {
|
||||
log.error("Uncaught Exception from thread " + Thread.currentThread().getName());
|
||||
log.error("throwableMessage= " + throwable.getMessage());
|
||||
log.error("throwableClass= " + throwable.getClass());
|
||||
log.error("Stack trace:\n" + ExceptionUtils.getStackTrace(throwable));
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
};
|
||||
Thread.setDefaultUncaughtExceptionHandler(handler);
|
||||
Thread.currentThread().setUncaughtExceptionHandler(handler);
|
||||
|
||||
if (Utilities.isRestrictedCryptography())
|
||||
Utilities.removeCryptographyRestrictions();
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
|
||||
|
||||
monitorModule = new MonitorModule(env);
|
||||
injector = Guice.createInjector(monitorModule);
|
||||
Version.setBtcNetworkId(injector.getInstance(BitsquareEnvironment.class).getBitcoinNetwork().ordinal());
|
||||
p2pService = injector.getInstance(P2PService.class);
|
||||
offerBookService = injector.getInstance(OfferBookService.class);
|
||||
openOfferManager = injector.getInstance(OpenOfferManager.class);
|
||||
p2pService.start(false, new P2PServiceListener() {
|
||||
@Override
|
||||
public void onRequestingDataCompleted() {
|
||||
openOfferManager.onAllServicesInitialized();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNoSeedNodeAvailable() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNoPeersAvailable() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBootstrapComplete() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTorNodeReady() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHiddenServicePublished() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetupFailed(Throwable throwable) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUseDefaultBridges() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestCustomBridges(Runnable resultHandler) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
gateway = new Gateway(offerBookService);
|
||||
}
|
||||
|
||||
public void shutDown() {
|
||||
gracefulShutDown(() -> {
|
||||
log.info("Shutdown complete");
|
||||
System.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
private void gracefulShutDown(ResultHandler resultHandler) {
|
||||
log.debug("gracefulShutDown");
|
||||
try {
|
||||
if (injector != null) {
|
||||
injector.getInstance(ArbitratorManager.class).shutDown();
|
||||
injector.getInstance(OpenOfferManager.class).shutDown(() -> {
|
||||
injector.getInstance(P2PService.class).shutDown(() -> {
|
||||
injector.getInstance(WalletService.class).shutDownDone.addListener((ov, o, n) -> {
|
||||
monitorModule.close(injector);
|
||||
log.info("Graceful shutdown completed");
|
||||
resultHandler.handleResult();
|
||||
});
|
||||
injector.getInstance(WalletService.class).shutDown();
|
||||
});
|
||||
});
|
||||
// we wait max 5 sec.
|
||||
UserThread.runAfter(resultHandler::handleResult, 5);
|
||||
} else {
|
||||
UserThread.runAfter(resultHandler::handleResult, 1);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
log.info("App shutdown failed with exception");
|
||||
t.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
99
monitor/src/main/java/io/bitsquare/monitor/MonitorMain.java
Normal file
99
monitor/src/main/java/io/bitsquare/monitor/MonitorMain.java
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.monitor;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import io.bitsquare.app.BitsquareEnvironment;
|
||||
import io.bitsquare.app.BitsquareExecutable;
|
||||
import io.bitsquare.common.UserThread;
|
||||
import joptsimple.OptionException;
|
||||
import joptsimple.OptionParser;
|
||||
import joptsimple.OptionSet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Scanner;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import static io.bitsquare.app.BitsquareEnvironment.*;
|
||||
|
||||
public class MonitorMain extends BitsquareExecutable {
|
||||
private static final Logger log = LoggerFactory.getLogger(MonitorMain.class);
|
||||
private Monitor monitor;
|
||||
private boolean isStopped;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
|
||||
.setNameFormat("MonitorMain")
|
||||
.setDaemon(true)
|
||||
.build();
|
||||
UserThread.setExecutor(Executors.newSingleThreadExecutor(threadFactory));
|
||||
|
||||
// We don't want to do the full argument parsing here as that might easily change in update versions
|
||||
// So we only handle the absolute minimum which is APP_NAME, APP_DATA_DIR_KEY and USER_DATA_DIR
|
||||
BitsquareEnvironment.setDefaultAppName("Bitsquare_monitor");
|
||||
OptionParser parser = new OptionParser();
|
||||
parser.allowsUnrecognizedOptions();
|
||||
parser.accepts(USER_DATA_DIR_KEY, description("User data directory", DEFAULT_USER_DATA_DIR))
|
||||
.withRequiredArg();
|
||||
parser.accepts(APP_NAME_KEY, description("Application name", DEFAULT_APP_NAME))
|
||||
.withRequiredArg();
|
||||
|
||||
OptionSet options;
|
||||
try {
|
||||
options = parser.parse(args);
|
||||
} catch (OptionException ex) {
|
||||
System.out.println("error: " + ex.getMessage());
|
||||
System.out.println();
|
||||
parser.printHelpOn(System.out);
|
||||
System.exit(EXIT_FAILURE);
|
||||
return;
|
||||
}
|
||||
BitsquareEnvironment bitsquareEnvironment = new BitsquareEnvironment(options);
|
||||
|
||||
// need to call that before BitsquareAppMain().execute(args)
|
||||
BitsquareExecutable.initAppDir(bitsquareEnvironment.getProperty(BitsquareEnvironment.APP_DATA_DIR_KEY));
|
||||
|
||||
// For some reason the JavaFX launch process results in us losing the thread context class loader: reset it.
|
||||
// In order to work around a bug in JavaFX 8u25 and below, you must include the following code as the first line of your realMain method:
|
||||
Thread.currentThread().setContextClassLoader(MonitorMain.class.getClassLoader());
|
||||
|
||||
new MonitorMain().execute(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(OptionSet options) {
|
||||
Monitor.setEnvironment(new BitsquareEnvironment(options));
|
||||
UserThread.execute(() -> monitor = new Monitor());
|
||||
|
||||
while (!isStopped) {
|
||||
try {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
while (scanner.hasNextLine()) {
|
||||
String inputString = scanner.nextLine();
|
||||
if (inputString.equals("q")) {
|
||||
UserThread.execute(monitor::shutDown);
|
||||
isStopped = true;
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
110
monitor/src/main/java/io/bitsquare/monitor/MonitorModule.java
Normal file
110
monitor/src/main/java/io/bitsquare/monitor/MonitorModule.java
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.monitor;
|
||||
|
||||
import com.google.inject.Singleton;
|
||||
import io.bitsquare.alert.AlertModule;
|
||||
import io.bitsquare.app.AppModule;
|
||||
import io.bitsquare.app.BitsquareEnvironment;
|
||||
import io.bitsquare.arbitration.ArbitratorModule;
|
||||
import io.bitsquare.btc.BitcoinModule;
|
||||
import io.bitsquare.common.Clock;
|
||||
import io.bitsquare.common.crypto.KeyRing;
|
||||
import io.bitsquare.common.crypto.KeyStorage;
|
||||
import io.bitsquare.crypto.EncryptionServiceModule;
|
||||
import io.bitsquare.filter.FilterModule;
|
||||
import io.bitsquare.p2p.P2PModule;
|
||||
import io.bitsquare.storage.Storage;
|
||||
import io.bitsquare.trade.TradeModule;
|
||||
import io.bitsquare.trade.offer.OfferModule;
|
||||
import io.bitsquare.user.Preferences;
|
||||
import io.bitsquare.user.User;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static com.google.inject.name.Names.named;
|
||||
|
||||
class MonitorModule extends AppModule {
|
||||
private static final Logger log = LoggerFactory.getLogger(MonitorModule.class);
|
||||
|
||||
public MonitorModule(Environment env) {
|
||||
super(env);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(KeyStorage.class).in(Singleton.class);
|
||||
bind(KeyRing.class).in(Singleton.class);
|
||||
bind(User.class).in(Singleton.class);
|
||||
bind(Preferences.class).in(Singleton.class);
|
||||
bind(Clock.class).in(Singleton.class);
|
||||
|
||||
File storageDir = new File(env.getRequiredProperty(Storage.DIR_KEY));
|
||||
bind(File.class).annotatedWith(named(Storage.DIR_KEY)).toInstance(storageDir);
|
||||
|
||||
File keyStorageDir = new File(env.getRequiredProperty(KeyStorage.DIR_KEY));
|
||||
bind(File.class).annotatedWith(named(KeyStorage.DIR_KEY)).toInstance(keyStorageDir);
|
||||
|
||||
bind(BitsquareEnvironment.class).toInstance((BitsquareEnvironment) env);
|
||||
|
||||
// ordering is used for shut down sequence
|
||||
install(tradeModule());
|
||||
install(encryptionServiceModule());
|
||||
install(arbitratorModule());
|
||||
install(offerModule());
|
||||
install(torModule());
|
||||
install(bitcoinModule());
|
||||
install(alertModule());
|
||||
install(filterModule());
|
||||
}
|
||||
|
||||
private TradeModule tradeModule() {
|
||||
return new TradeModule(env);
|
||||
}
|
||||
|
||||
private EncryptionServiceModule encryptionServiceModule() {
|
||||
return new EncryptionServiceModule(env);
|
||||
}
|
||||
|
||||
private ArbitratorModule arbitratorModule() {
|
||||
return new ArbitratorModule(env);
|
||||
}
|
||||
|
||||
private AlertModule alertModule() {
|
||||
return new AlertModule(env);
|
||||
}
|
||||
|
||||
private FilterModule filterModule() {
|
||||
return new FilterModule(env);
|
||||
}
|
||||
|
||||
private OfferModule offerModule() {
|
||||
return new OfferModule(env);
|
||||
}
|
||||
|
||||
private P2PModule torModule() {
|
||||
return new P2PModule(env);
|
||||
}
|
||||
|
||||
private BitcoinModule bitcoinModule() {
|
||||
return new BitcoinModule(env);
|
||||
}
|
||||
}
|
42
monitor/src/main/resources/logback.xml
Normal file
42
monitor/src/main/resources/logback.xml
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ This file is part of Bitsquare.
|
||||
~
|
||||
~ Bitsquare is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU Affero General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or (at
|
||||
~ your option) any later version.
|
||||
~
|
||||
~ Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
~ License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU Affero General Public License
|
||||
~ along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<configuration>
|
||||
<appender name="CONSOLE_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%highlight(%d{MMM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{15}: %msg %xEx%n)</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="TRACE">
|
||||
<appender-ref ref="CONSOLE_APPENDER"/>
|
||||
</root>
|
||||
|
||||
<logger name="io.bitsquare.storage.Storage" level="WARN"/>
|
||||
<logger name="io.bitsquare.storage.FileManager" level="WARN"/>
|
||||
|
||||
<!-- <logger name="io.bitsquare.p2p.peers.PeerGroup" level="INFO"/>
|
||||
<logger name="io.bitsquare.p2p.P2PService" level="INFO"/>
|
||||
<logger name="io.bitsquare.p2p.storage.ProtectedExpirableDataStorage" level="INFO"/>
|
||||
<logger name="io.bitsquare.p2p.network.LocalhostNetworkNode" level="INFO"/>
|
||||
<logger name="io.bitsquare.p2p.network.TorNetworkNode" level="TRACE"/>
|
||||
<logger name="io.bitsquare.p2p.network.NetworkNode" level="TRACE"/>-->
|
||||
|
||||
<logger name="com.msopentech.thali.toronionproxy.OnionProxyManagerEventHandler" level="INFO"/>
|
||||
|
||||
</configuration>
|
|
@ -1,16 +1,14 @@
|
|||
package io.bitsquare.network;
|
||||
|
||||
public class OptionKeys {
|
||||
public class NetworkOptionKeys {
|
||||
public static final String TOR_DIR = "torDir";
|
||||
public static final String USE_LOCALHOST = "useLocalhost";
|
||||
public static final String MAX_CONNECTIONS = "maxConnections";
|
||||
public static final String PORT_KEY = "nodePort";
|
||||
public static final String NETWORK_ID = "networkId";
|
||||
public static final String SEED_NODES_KEY = "seedNodes";
|
||||
|
||||
// Seed nodes
|
||||
public static final String MY_ADDRESS = "myAddress";
|
||||
public static final String SEED_NODES_LIST = "seedNodes";
|
||||
public static final String BAN_LIST = "banList";
|
||||
public static final String HELP = "help";
|
||||
|
||||
|
||||
}
|
|
@ -37,6 +37,11 @@ public final class NodeAddress implements Persistable, Payload {
|
|||
return addressPrefixHash;
|
||||
}
|
||||
|
||||
public String getHostNameWithoutPostFix() {
|
||||
return hostName.replace(".onion", "");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
|
|
@ -20,7 +20,7 @@ package io.bitsquare.p2p;
|
|||
import com.google.inject.Singleton;
|
||||
import com.google.inject.name.Names;
|
||||
import io.bitsquare.app.AppModule;
|
||||
import io.bitsquare.network.OptionKeys;
|
||||
import io.bitsquare.network.NetworkOptionKeys;
|
||||
import io.bitsquare.p2p.seed.SeedNodesRepository;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
|
@ -40,21 +40,23 @@ public class P2PModule extends AppModule {
|
|||
bind(SeedNodesRepository.class).in(Singleton.class);
|
||||
bind(P2PService.class).in(Singleton.class);
|
||||
|
||||
Boolean useLocalhost = env.getProperty(OptionKeys.USE_LOCALHOST, boolean.class, false);
|
||||
bind(boolean.class).annotatedWith(Names.named(OptionKeys.USE_LOCALHOST)).toInstance(useLocalhost);
|
||||
Boolean useLocalhost = env.getProperty(NetworkOptionKeys.USE_LOCALHOST, boolean.class, false);
|
||||
bind(boolean.class).annotatedWith(Names.named(NetworkOptionKeys.USE_LOCALHOST)).toInstance(useLocalhost);
|
||||
|
||||
File torDir = new File(env.getRequiredProperty(OptionKeys.TOR_DIR));
|
||||
bind(File.class).annotatedWith(named(OptionKeys.TOR_DIR)).toInstance(torDir);
|
||||
File torDir = new File(env.getRequiredProperty(NetworkOptionKeys.TOR_DIR));
|
||||
bind(File.class).annotatedWith(named(NetworkOptionKeys.TOR_DIR)).toInstance(torDir);
|
||||
|
||||
// use a fixed port as arbitrator use that for his ID
|
||||
Integer port = env.getProperty(OptionKeys.PORT_KEY, int.class, 9999);
|
||||
bind(int.class).annotatedWith(Names.named(OptionKeys.PORT_KEY)).toInstance(port);
|
||||
Integer port = env.getProperty(NetworkOptionKeys.PORT_KEY, int.class, 9999);
|
||||
bind(int.class).annotatedWith(Names.named(NetworkOptionKeys.PORT_KEY)).toInstance(port);
|
||||
|
||||
Integer maxConnections = env.getProperty(OptionKeys.MAX_CONNECTIONS, int.class, P2PService.MAX_CONNECTIONS_DEFAULT);
|
||||
bind(int.class).annotatedWith(Names.named(OptionKeys.MAX_CONNECTIONS)).toInstance(maxConnections);
|
||||
Integer maxConnections = env.getProperty(NetworkOptionKeys.MAX_CONNECTIONS, int.class, P2PService.MAX_CONNECTIONS_DEFAULT);
|
||||
bind(int.class).annotatedWith(Names.named(NetworkOptionKeys.MAX_CONNECTIONS)).toInstance(maxConnections);
|
||||
|
||||
Integer networkId = env.getProperty(OptionKeys.NETWORK_ID, int.class, 1);
|
||||
bind(int.class).annotatedWith(Names.named(OptionKeys.NETWORK_ID)).toInstance(networkId);
|
||||
bindConstant().annotatedWith(named(OptionKeys.SEED_NODES_KEY)).to(env.getRequiredProperty(OptionKeys.SEED_NODES_KEY));
|
||||
Integer networkId = env.getProperty(NetworkOptionKeys.NETWORK_ID, int.class, 1);
|
||||
bind(int.class).annotatedWith(Names.named(NetworkOptionKeys.NETWORK_ID)).toInstance(networkId);
|
||||
bindConstant().annotatedWith(named(NetworkOptionKeys.SEED_NODES_KEY)).to(env.getRequiredProperty(NetworkOptionKeys.SEED_NODES_KEY));
|
||||
bindConstant().annotatedWith(named(NetworkOptionKeys.MY_ADDRESS)).to(env.getRequiredProperty(NetworkOptionKeys.MY_ADDRESS));
|
||||
bindConstant().annotatedWith(named(NetworkOptionKeys.BAN_LIST)).to(env.getRequiredProperty(NetworkOptionKeys.BAN_LIST));
|
||||
}
|
||||
}
|
|
@ -14,9 +14,10 @@ import io.bitsquare.common.crypto.KeyRing;
|
|||
import io.bitsquare.common.crypto.PubKeyRing;
|
||||
import io.bitsquare.crypto.DecryptedMsgWithPubKey;
|
||||
import io.bitsquare.crypto.EncryptionService;
|
||||
import io.bitsquare.network.OptionKeys;
|
||||
import io.bitsquare.network.NetworkOptionKeys;
|
||||
import io.bitsquare.p2p.messaging.*;
|
||||
import io.bitsquare.p2p.network.*;
|
||||
import io.bitsquare.p2p.peers.BanList;
|
||||
import io.bitsquare.p2p.peers.BroadcastHandler;
|
||||
import io.bitsquare.p2p.peers.Broadcaster;
|
||||
import io.bitsquare.p2p.peers.PeerManager;
|
||||
|
@ -64,6 +65,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
|||
private final int maxConnections;
|
||||
private final File torDir;
|
||||
private Clock clock;
|
||||
//TODO optional can be removed as seednode are created with those objects now
|
||||
private final Optional<EncryptionService> optionalEncryptionService;
|
||||
private final Optional<KeyRing> optionalKeyRing;
|
||||
|
||||
|
@ -100,13 +102,15 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
|||
// Called also from SeedNodeP2PService
|
||||
@Inject
|
||||
public P2PService(SeedNodesRepository seedNodesRepository,
|
||||
@Named(OptionKeys.PORT_KEY) int port,
|
||||
@Named(OptionKeys.TOR_DIR) File torDir,
|
||||
@Named(OptionKeys.USE_LOCALHOST) boolean useLocalhost,
|
||||
@Named(OptionKeys.NETWORK_ID) int networkId,
|
||||
@Named(OptionKeys.MAX_CONNECTIONS) int maxConnections,
|
||||
@Named(NetworkOptionKeys.PORT_KEY) int port,
|
||||
@Named(NetworkOptionKeys.TOR_DIR) File torDir,
|
||||
@Named(NetworkOptionKeys.USE_LOCALHOST) boolean useLocalhost,
|
||||
@Named(NetworkOptionKeys.NETWORK_ID) int networkId,
|
||||
@Named(NetworkOptionKeys.MAX_CONNECTIONS) int maxConnections,
|
||||
@Named(Storage.DIR_KEY) File storageDir,
|
||||
@Named(OptionKeys.SEED_NODES_KEY) String seedNodes,
|
||||
@Named(NetworkOptionKeys.SEED_NODES_KEY) String seedNodes,
|
||||
@Named(NetworkOptionKeys.MY_ADDRESS) String myAddress,
|
||||
@Named(NetworkOptionKeys.BAN_LIST) String banList,
|
||||
Clock clock,
|
||||
@Nullable EncryptionService encryptionService,
|
||||
@Nullable KeyRing keyRing) {
|
||||
|
@ -119,6 +123,8 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
|||
networkId,
|
||||
storageDir,
|
||||
seedNodes,
|
||||
myAddress,
|
||||
banList,
|
||||
clock,
|
||||
encryptionService,
|
||||
keyRing
|
||||
|
@ -133,6 +139,8 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
|||
int networkId,
|
||||
File storageDir,
|
||||
String seedNodes,
|
||||
String myAddress,
|
||||
String banList,
|
||||
Clock clock,
|
||||
@Nullable EncryptionService encryptionService,
|
||||
@Nullable KeyRing keyRing) {
|
||||
|
@ -145,13 +153,28 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
|||
optionalEncryptionService = Optional.ofNullable(encryptionService);
|
||||
optionalKeyRing = Optional.ofNullable(keyRing);
|
||||
|
||||
init(useLocalhost, networkId, storageDir, seedNodes);
|
||||
init(useLocalhost,
|
||||
networkId,
|
||||
storageDir,
|
||||
seedNodes,
|
||||
myAddress,
|
||||
banList);
|
||||
}
|
||||
|
||||
private void init(boolean useLocalhost, int networkId, File storageDir, String seedNodes) {
|
||||
private void init(boolean useLocalhost,
|
||||
int networkId,
|
||||
File storageDir,
|
||||
String seedNodes,
|
||||
String myAddress,
|
||||
String banList) {
|
||||
if (!useLocalhost)
|
||||
FileUtil.rollingBackup(new File(Paths.get(torDir.getAbsolutePath(), "hiddenservice").toString()), "private_key");
|
||||
|
||||
if (banList != null && !banList.isEmpty())
|
||||
BanList.setList(Arrays.asList(banList.replace(" ", "").split(",")).stream().map(NodeAddress::new).collect(Collectors.toList()));
|
||||
if (myAddress != null && !myAddress.isEmpty())
|
||||
seedNodesRepository.setNodeAddressToExclude(new NodeAddress(myAddress));
|
||||
|
||||
networkNode = useLocalhost ? new LocalhostNetworkNode(port) : new TorNetworkNode(port, torDir);
|
||||
networkNode.addConnectionListener(this);
|
||||
networkNode.addMessageListener(this);
|
||||
|
@ -199,6 +222,25 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
|||
networkNode.start(useBridges, this);
|
||||
}
|
||||
|
||||
public void onAllServicesInitialized() {
|
||||
Log.traceCall();
|
||||
if (networkNode.getNodeAddress() != null) {
|
||||
p2PDataStorage.getMap().values().stream().forEach(protectedStorageEntry -> {
|
||||
if (protectedStorageEntry instanceof ProtectedMailboxStorageEntry)
|
||||
processProtectedMailboxStorageEntry((ProtectedMailboxStorageEntry) protectedStorageEntry);
|
||||
});
|
||||
} else {
|
||||
networkNode.nodeAddressProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue != null) {
|
||||
p2PDataStorage.getMap().values().stream().forEach(protectedStorageEntry -> {
|
||||
if (protectedStorageEntry instanceof ProtectedMailboxStorageEntry)
|
||||
processProtectedMailboxStorageEntry((ProtectedMailboxStorageEntry) protectedStorageEntry);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void shutDown(Runnable shutDownCompleteHandler) {
|
||||
Log.traceCall();
|
||||
if (!shutDownInProgress) {
|
||||
|
@ -488,8 +530,10 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void processProtectedMailboxStorageEntry(ProtectedMailboxStorageEntry protectedMailboxStorageEntry) {
|
||||
// Seed nodes don't have set the encryptionService
|
||||
if (optionalEncryptionService.isPresent()) {
|
||||
Log.traceCall();
|
||||
final NodeAddress nodeAddress = networkNode.getNodeAddress();
|
||||
// Seed nodes don't receive mailbox messages
|
||||
if (optionalEncryptionService.isPresent() && nodeAddress != null && !seedNodesRepository.isSeedNode(nodeAddress)) {
|
||||
Log.traceCall();
|
||||
MailboxStoragePayload mailboxStoragePayload = protectedMailboxStorageEntry.getMailboxStoragePayload();
|
||||
PrefixedSealedAndSignedMessage prefixedSealedAndSignedMessage = mailboxStoragePayload.prefixedSealedAndSignedMessage;
|
||||
|
|
|
@ -44,8 +44,9 @@ public final class PrefixedSealedAndSignedMessage implements MailboxMessage, Sen
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SealedAndSignedMessage{" +
|
||||
"messageVersion=" + messageVersion +
|
||||
return "PrefixedSealedAndSignedMessage{" +
|
||||
"uid=" + uid +
|
||||
", messageVersion=" + messageVersion +
|
||||
", sealedAndSigned=" + sealedAndSigned +
|
||||
", receiverAddressMaskHash.hashCode()=" + Arrays.toString(addressPrefixHash).hashCode() +
|
||||
'}';
|
||||
|
|
|
@ -70,7 +70,7 @@ public class Connection implements MessageListener {
|
|||
//TODO decrease limits again after testing
|
||||
static final int MSG_THROTTLE_PER_SEC = 200; // With MAX_MSG_SIZE of 200kb results in bandwidth of 40MB/sec or 5 mbit/sec
|
||||
static final int MSG_THROTTLE_PER_10_SEC = 1000; // With MAX_MSG_SIZE of 200kb results in bandwidth of 20MB/sec or 2.5 mbit/sec
|
||||
private static final int SOCKET_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(90);
|
||||
private static final int SOCKET_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(60);
|
||||
|
||||
public static int getMaxMsgSize() {
|
||||
return MAX_MSG_SIZE;
|
||||
|
@ -169,6 +169,7 @@ public class Connection implements MessageListener {
|
|||
public void sendMessage(Message message) {
|
||||
if (!stopped) {
|
||||
try {
|
||||
log.info("sendMessage message=" + getTruncatedMessage(message));
|
||||
Log.traceCall();
|
||||
// Throttle outbound messages
|
||||
long now = System.currentTimeMillis();
|
||||
|
@ -190,7 +191,7 @@ public class Connection implements MessageListener {
|
|||
"Sending direct message to peer" +
|
||||
"Write object to outputStream to peer: {} (uid={})\ntruncated message={} / size={}" +
|
||||
"\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n",
|
||||
peersNodeAddress, uid, StringUtils.abbreviate(message.toString(), 100), size);
|
||||
peersNodeAddress, uid, getTruncatedMessage(message), size);
|
||||
} else if (message instanceof PrefixedSealedAndSignedMessage && peersNodeAddressOptional.isPresent()) {
|
||||
setPeerType(Connection.PeerType.DIRECT_MSG_PEER);
|
||||
|
||||
|
@ -198,12 +199,12 @@ public class Connection implements MessageListener {
|
|||
"Sending direct message to peer" +
|
||||
"Write object to outputStream to peer: {} (uid={})\ntruncated message={} / size={}" +
|
||||
"\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n",
|
||||
peersNodeAddress, uid, StringUtils.abbreviate(message.toString(), 100), size);
|
||||
peersNodeAddress, uid, getTruncatedMessage(message), size);
|
||||
} else {
|
||||
log.info("\n\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" +
|
||||
"Write object to outputStream to peer: {} (uid={})\ntruncated message={} / size={}" +
|
||||
"\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n",
|
||||
peersNodeAddress, uid, StringUtils.abbreviate(message.toString(), 100), size);
|
||||
peersNodeAddress, uid, getTruncatedMessage(message), size);
|
||||
}
|
||||
|
||||
if (!stopped) {
|
||||
|
@ -450,6 +451,10 @@ public class Connection implements MessageListener {
|
|||
}
|
||||
}
|
||||
|
||||
private String getTruncatedMessage(Message message) {
|
||||
return StringUtils.abbreviate(message.toString(), 100).replace("\n", "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
@ -689,6 +694,10 @@ public class Connection implements MessageListener {
|
|||
+ "\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n",
|
||||
connection,
|
||||
size);
|
||||
try {
|
||||
log.error("rawInputObject.className=" + rawInputObject.getClass().getName());
|
||||
} catch (Throwable ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
// We want to track the size of each object even if it is invalid data
|
||||
|
|
|
@ -80,8 +80,7 @@ public class LocalhostNetworkNode extends NetworkNode {
|
|||
protected Socket createSocket(NodeAddress peerNodeAddress) throws IOException {
|
||||
return new Socket(peerNodeAddress.hostName, peerNodeAddress.port);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Tor delay simulation
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package io.bitsquare.p2p.network;
|
||||
|
||||
import com.google.common.util.concurrent.*;
|
||||
import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;
|
||||
import io.bitsquare.app.Log;
|
||||
import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.common.util.Utilities;
|
||||
|
@ -213,6 +214,12 @@ public abstract class NetworkNode implements MessageListener {
|
|||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Socks5Proxy getSocksProxy() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public SettableFuture<Connection> sendMessage(Connection connection, Message message) {
|
||||
Log.traceCall("\n\tmessage=" + StringUtils.abbreviate(message.toString(), 100) + "\n\tconnection=" + connection);
|
||||
// connection.sendMessage might take a bit (compression, write to stream), so we use a thread to not block
|
||||
|
|
|
@ -6,6 +6,7 @@ import com.google.common.util.concurrent.ListenableFuture;
|
|||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import com.msopentech.thali.java.toronionproxy.JavaOnionProxyContext;
|
||||
import com.msopentech.thali.java.toronionproxy.JavaOnionProxyManager;
|
||||
import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;
|
||||
import io.bitsquare.app.Log;
|
||||
import io.bitsquare.common.Timer;
|
||||
import io.bitsquare.common.UserThread;
|
||||
|
@ -95,10 +96,15 @@ public class TorNetworkNode extends NetworkNode {
|
|||
|
||||
@Override
|
||||
protected Socket createSocket(NodeAddress peerNodeAddress) throws IOException {
|
||||
checkArgument(peerNodeAddress.hostName.endsWith(".onion"), "PeerAddress is not an onion address");
|
||||
// FIXME: disabling temporarily.
|
||||
// checkArgument(peerNodeAddress.hostName.endsWith(".onion"), "PeerAddress is not an onion address");
|
||||
|
||||
return torNetworkNode.connectToHiddenService(peerNodeAddress.hostName, peerNodeAddress.port);
|
||||
}
|
||||
|
||||
public Socks5Proxy getSocksProxy() {
|
||||
return torNetworkNode != null ? torNetworkNode.getSocksProxy() : null;
|
||||
}
|
||||
|
||||
public void shutDown(@Nullable Runnable shutDownCompleteHandler) {
|
||||
Log.traceCall();
|
||||
|
|
|
@ -120,14 +120,14 @@ public class BroadcastHandler implements PeerManager.Listener {
|
|||
timeoutTimer = UserThread.runAfter(() -> { // setup before sending to avoid race conditions
|
||||
String errorMessage = "Timeout: Broadcast did not complete after " + timeoutDelay + " sec.";
|
||||
|
||||
log.warn(errorMessage + "\n\t" +
|
||||
log.debug(errorMessage + "\n\t" +
|
||||
"numOfPeers=" + numOfPeers + "\n\t" +
|
||||
"numOfCompletedBroadcasts=" + numOfCompletedBroadcasts + "\n\t" +
|
||||
"numOfCompletedBroadcasts=" + numOfCompletedBroadcasts + "\n\t" +
|
||||
"numOfFailedBroadcasts=" + numOfFailedBroadcasts + "\n\t" +
|
||||
"broadcastQueue.size()=" + broadcastQueue.size() + "\n\t" +
|
||||
"broadcastQueue=" + broadcastQueue);
|
||||
onFault(errorMessage);
|
||||
onFault(errorMessage, false);
|
||||
}, timeoutDelay);
|
||||
|
||||
log.info("Broadcast message to {} peers out of {} total connected peers.", numOfPeers, connectedPeersSet.size());
|
||||
|
@ -196,10 +196,10 @@ public class BroadcastHandler implements PeerManager.Listener {
|
|||
}
|
||||
});
|
||||
} else {
|
||||
onFault("Connection stopped already");
|
||||
onFault("Connection stopped already", false);
|
||||
}
|
||||
} else {
|
||||
onFault("stopped at sendToPeer: " + errorMessage);
|
||||
onFault("stopped at sendToPeer: " + errorMessage, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,7 +210,7 @@ public class BroadcastHandler implements PeerManager.Listener {
|
|||
|
||||
@Override
|
||||
public void onAllConnectionsLost() {
|
||||
onFault("All connections lost");
|
||||
onFault("All connections lost", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -337,7 +337,8 @@ public class PeerManager implements ConnectionListener {
|
|||
|
||||
@Nullable
|
||||
private Peer removeReportedPeer(NodeAddress nodeAddress) {
|
||||
Optional<Peer> reportedPeerOptional = reportedPeers.stream()
|
||||
List<Peer> reportedPeersClone = new ArrayList<>(reportedPeers);
|
||||
Optional<Peer> reportedPeerOptional = reportedPeersClone.stream()
|
||||
.filter(e -> e.nodeAddress.equals(nodeAddress)).findAny();
|
||||
if (reportedPeerOptional.isPresent()) {
|
||||
Peer reportedPeer = reportedPeerOptional.get();
|
||||
|
@ -350,7 +351,8 @@ public class PeerManager implements ConnectionListener {
|
|||
|
||||
private void removeTooOldReportedPeers() {
|
||||
Log.traceCall();
|
||||
Set<Peer> reportedPeersToRemove = reportedPeers.stream()
|
||||
List<Peer> reportedPeersClone = new ArrayList<>(reportedPeers);
|
||||
Set<Peer> reportedPeersToRemove = reportedPeersClone.stream()
|
||||
.filter(reportedPeer -> new Date().getTime() - reportedPeer.date.getTime() > MAX_AGE)
|
||||
.collect(Collectors.toSet());
|
||||
reportedPeersToRemove.forEach(this::removeReportedPeer);
|
||||
|
@ -406,9 +408,10 @@ public class PeerManager implements ConnectionListener {
|
|||
if (printReportedPeersDetails) {
|
||||
StringBuilder result = new StringBuilder("\n\n------------------------------------------------------------\n" +
|
||||
"Collected reported peers:");
|
||||
reportedPeers.stream().forEach(e -> result.append("\n").append(e));
|
||||
List<Peer> reportedPeersClone = new ArrayList<>(reportedPeers);
|
||||
reportedPeersClone.stream().forEach(e -> result.append("\n").append(e));
|
||||
result.append("\n------------------------------------------------------------\n");
|
||||
log.info(result.toString());
|
||||
log.debug(result.toString());
|
||||
}
|
||||
log.info("Number of collected reported peers: {}", reportedPeers.size());
|
||||
}
|
||||
|
@ -417,8 +420,9 @@ public class PeerManager implements ConnectionListener {
|
|||
private void printNewReportedPeers(HashSet<Peer> reportedPeers) {
|
||||
if (printReportedPeersDetails) {
|
||||
StringBuilder result = new StringBuilder("We received new reportedPeers:");
|
||||
reportedPeers.stream().forEach(e -> result.append("\n\t").append(e));
|
||||
log.info(result.toString());
|
||||
List<Peer> reportedPeersClone = new ArrayList<>(reportedPeers);
|
||||
reportedPeersClone.stream().forEach(e -> result.append("\n\t").append(e));
|
||||
log.debug(result.toString());
|
||||
}
|
||||
log.info("Number of new arrived reported peers: {}", reportedPeers.size());
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ public class RequestDataManager implements MessageListener, ConnectionListener,
|
|||
|
||||
public void requestUpdateData() {
|
||||
Log.traceCall();
|
||||
checkArgument(nodeAddressOfPreliminaryDataRequest.isPresent(), "seedNodeOfPreliminaryDataRequest must be present");
|
||||
checkArgument(nodeAddressOfPreliminaryDataRequest.isPresent(), "nodeAddressOfPreliminaryDataRequest must be present");
|
||||
dataUpdateRequested = true;
|
||||
List<NodeAddress> remainingNodeAddresses = new ArrayList<>(seedNodeAddresses);
|
||||
if (!remainingNodeAddresses.isEmpty()) {
|
||||
|
|
|
@ -196,10 +196,10 @@ public class KeepAliveManager implements MessageListener, ConnectionListener, Pe
|
|||
});
|
||||
|
||||
int size = handlerMap.size();
|
||||
log.info("maintenanceHandlerMap size=" + size);
|
||||
log.info("handlerMap size=" + size);
|
||||
if (size > peerManager.getMaxConnections())
|
||||
log.warn("Seems we didn't clean up out map correctly.\n" +
|
||||
"maintenanceHandlerMap size={}, peerManager.getMaxConnections()={}", size, peerManager.getMaxConnections());
|
||||
"handlerMap size={}, peerManager.getMaxConnections()={}", size, peerManager.getMaxConnections());
|
||||
} else {
|
||||
log.warn("We have stopped already. We ignore that keepAlive call.");
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class SeedNodesRepository {
|
||||
private static final Logger log = LoggerFactory.getLogger(SeedNodesRepository.class);
|
||||
|
@ -95,6 +96,11 @@ public class SeedNodesRepository {
|
|||
this.localhostSeedNodeAddresses = localhostSeedNodeAddresses;
|
||||
}
|
||||
|
||||
public boolean isSeedNode(NodeAddress nodeAddress) {
|
||||
return Stream.concat(localhostSeedNodeAddresses.stream(), torSeedNodeAddresses.stream())
|
||||
.filter(e -> e.equals(nodeAddress)).findAny().isPresent();
|
||||
}
|
||||
|
||||
public void setNodeAddressToExclude(NodeAddress nodeAddress) {
|
||||
this.nodeAddressToExclude = nodeAddress;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import io.bitsquare.common.crypto.CryptoException;
|
|||
import io.bitsquare.common.crypto.Hash;
|
||||
import io.bitsquare.common.crypto.Sig;
|
||||
import io.bitsquare.common.persistance.Persistable;
|
||||
import io.bitsquare.common.util.Tuple2;
|
||||
import io.bitsquare.common.wire.Payload;
|
||||
import io.bitsquare.p2p.Message;
|
||||
import io.bitsquare.p2p.NodeAddress;
|
||||
|
@ -36,6 +37,7 @@ import java.util.*;
|
|||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
// Run in UserThread
|
||||
public class P2PDataStorage implements MessageListener, ConnectionListener {
|
||||
|
@ -95,7 +97,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
|||
ByteArray hashOfPayload = entry.getKey();
|
||||
ProtectedStorageEntry protectedStorageEntry = map.get(hashOfPayload);
|
||||
toRemoveSet.add(protectedStorageEntry);
|
||||
log.info("We found an expired data entry. We remove the protectedData:\n\t" + protectedStorageEntry);
|
||||
log.info("We found an expired data entry. We remove the protectedData:\n\t" + StringUtils.abbreviate(protectedStorageEntry.toString().replace("\n", ""), 100));
|
||||
map.remove(hashOfPayload);
|
||||
});
|
||||
|
||||
|
@ -210,24 +212,26 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
|||
if (containsKey)
|
||||
result &= checkIfStoredDataPubKeyMatchesNewDataPubKey(protectedStorageEntry.ownerPubKey, hashOfPayload);
|
||||
|
||||
// printData("before add");
|
||||
if (result) {
|
||||
map.put(hashOfPayload, protectedStorageEntry);
|
||||
final boolean hasSequenceNrIncreased = hasSequenceNrIncreased(protectedStorageEntry.sequenceNumber, hashOfPayload);
|
||||
if (!containsKey || hasSequenceNrIncreased) {
|
||||
// At startup we don't have the item so we store it. At updates of the seq nr we store as well.
|
||||
map.put(hashOfPayload, protectedStorageEntry);
|
||||
hashMapChangedListeners.stream().forEach(e -> e.onAdded(protectedStorageEntry));
|
||||
printData("after add");
|
||||
} else {
|
||||
log.trace("We got that version of the data already, so we don't store it.");
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder("\n\n------------------------------------------------------------\n");
|
||||
sb.append("Data set after doAdd (truncated)");
|
||||
map.values().stream().forEach(e -> sb.append("\n").append(StringUtils.abbreviate(e.toString(), 100)));
|
||||
sb.append("\n------------------------------------------------------------\n");
|
||||
log.trace(sb.toString());
|
||||
log.info("Data set after doAdd: size=" + map.values().size());
|
||||
|
||||
if (hasSequenceNrIncreased(protectedStorageEntry.sequenceNumber, hashOfPayload)) {
|
||||
if (hasSequenceNrIncreased) {
|
||||
sequenceNumberMap.put(hashOfPayload, new MapValue(protectedStorageEntry.sequenceNumber, System.currentTimeMillis()));
|
||||
storage.queueUpForSave(sequenceNumberMap, 100);
|
||||
|
||||
broadcast(new AddDataMessage(protectedStorageEntry), sender, listener, isDataOwner);
|
||||
} else {
|
||||
log.trace("We got that version of the data already, so we don't broadcast it.");
|
||||
}
|
||||
|
||||
hashMapChangedListeners.stream().forEach(e -> e.onAdded(protectedStorageEntry));
|
||||
} else {
|
||||
log.trace("add failed");
|
||||
}
|
||||
|
@ -250,29 +254,26 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
|||
return true;
|
||||
} else {
|
||||
PublicKey ownerPubKey = storedData.getStoragePayload().getOwnerPubKey();
|
||||
boolean result = checkSignature(ownerPubKey, hashOfDataAndSeqNr, signature) &&
|
||||
hasSequenceNrIncreased(sequenceNumber, hashOfPayload) &&
|
||||
checkIfStoredDataPubKeyMatchesNewDataPubKey(ownerPubKey, hashOfPayload);
|
||||
final boolean checkSignature = checkSignature(ownerPubKey, hashOfDataAndSeqNr, signature);
|
||||
final boolean hasSequenceNrIncreased = hasSequenceNrIncreased(sequenceNumber, hashOfPayload);
|
||||
final boolean checkIfStoredDataPubKeyMatchesNewDataPubKey = checkIfStoredDataPubKeyMatchesNewDataPubKey(ownerPubKey, hashOfPayload);
|
||||
boolean allValid = checkSignature &&
|
||||
hasSequenceNrIncreased &&
|
||||
checkIfStoredDataPubKeyMatchesNewDataPubKey;
|
||||
|
||||
if (result) {
|
||||
log.info("refreshDate called for storedData:\n\t" + StringUtils.abbreviate(storedData.toString(), 100));
|
||||
// printData("before refreshTTL");
|
||||
if (allValid) {
|
||||
log.debug("refreshDate called for storedData:\n\t" + StringUtils.abbreviate(storedData.toString(), 100));
|
||||
storedData.refreshTTL();
|
||||
storedData.updateSequenceNumber(sequenceNumber);
|
||||
storedData.updateSignature(signature);
|
||||
|
||||
printData("after refreshTTL");
|
||||
sequenceNumberMap.put(hashOfPayload, new MapValue(sequenceNumber, System.currentTimeMillis()));
|
||||
storage.queueUpForSave(sequenceNumberMap, 100);
|
||||
|
||||
StringBuilder sb = new StringBuilder("\n\n------------------------------------------------------------\n");
|
||||
sb.append("Data set after refreshTTL (truncated)");
|
||||
map.values().stream().forEach(e -> sb.append("\n").append(StringUtils.abbreviate(e.toString(), 100)));
|
||||
sb.append("\n------------------------------------------------------------\n");
|
||||
log.trace(sb.toString());
|
||||
log.info("Data set after refreshTTL: size=" + map.values().size());
|
||||
|
||||
broadcast(refreshTTLMessage, sender, null, isDataOwner);
|
||||
}
|
||||
return result;
|
||||
return allValid;
|
||||
}
|
||||
} else {
|
||||
log.debug("We don't have data for that refresh message in our map. That is expected if we missed the data publishing.");
|
||||
|
@ -292,14 +293,14 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
|||
&& checkSignature(protectedStorageEntry)
|
||||
&& checkIfStoredDataPubKeyMatchesNewDataPubKey(protectedStorageEntry.ownerPubKey, hashOfPayload);
|
||||
|
||||
|
||||
// printData("before remove");
|
||||
if (result) {
|
||||
doRemoveProtectedExpirableData(protectedStorageEntry, hashOfPayload);
|
||||
|
||||
broadcast(new RemoveDataMessage(protectedStorageEntry), sender, null, isDataOwner);
|
||||
|
||||
printData("after remove");
|
||||
sequenceNumberMap.put(hashOfPayload, new MapValue(protectedStorageEntry.sequenceNumber, System.currentTimeMillis()));
|
||||
storage.queueUpForSave(sequenceNumberMap, 100);
|
||||
|
||||
broadcast(new RemoveDataMessage(protectedStorageEntry), sender, null, isDataOwner);
|
||||
} else {
|
||||
log.debug("remove failed");
|
||||
}
|
||||
|
@ -319,13 +320,14 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
|||
&& checkSignature(protectedMailboxStorageEntry)
|
||||
&& checkIfStoredMailboxDataMatchesNewMailboxData(protectedMailboxStorageEntry.receiversPubKey, hashOfData);
|
||||
|
||||
// printData("before removeMailboxData");
|
||||
if (result) {
|
||||
doRemoveProtectedExpirableData(protectedMailboxStorageEntry, hashOfData);
|
||||
|
||||
broadcast(new RemoveMailboxDataMessage(protectedMailboxStorageEntry), sender, null, isDataOwner);
|
||||
|
||||
printData("after removeMailboxData");
|
||||
sequenceNumberMap.put(hashOfData, new MapValue(protectedMailboxStorageEntry.sequenceNumber, System.currentTimeMillis()));
|
||||
storage.queueUpForSave(sequenceNumberMap, 100);
|
||||
|
||||
broadcast(new RemoveMailboxDataMessage(protectedMailboxStorageEntry), sender, null, isDataOwner);
|
||||
} else {
|
||||
log.debug("removeMailboxData failed");
|
||||
}
|
||||
|
@ -394,13 +396,6 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
|||
map.remove(hashOfPayload);
|
||||
log.trace("Data removed from our map. We broadcast the message to our peers.");
|
||||
hashMapChangedListeners.stream().forEach(e -> e.onRemoved(protectedStorageEntry));
|
||||
|
||||
StringBuilder sb = new StringBuilder("\n\n------------------------------------------------------------\n" +
|
||||
"Data set after removeProtectedExpirableData: (truncated)");
|
||||
map.values().stream().forEach(e -> sb.append("\n").append(StringUtils.abbreviate(e.toString(), 100)));
|
||||
sb.append("\n------------------------------------------------------------\n");
|
||||
log.trace(sb.toString());
|
||||
log.info("Data set after doRemoveProtectedExpirableData: size=" + map.values().size());
|
||||
}
|
||||
|
||||
private boolean isSequenceNrValid(int newSequenceNumber, ByteArray hashOfData) {
|
||||
|
@ -425,15 +420,16 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
|||
if (newSequenceNumber > storedSequenceNumber) {
|
||||
return true;
|
||||
} else if (newSequenceNumber == storedSequenceNumber) {
|
||||
String msg;
|
||||
if (newSequenceNumber == 0) {
|
||||
log.debug("Sequence number is equal to the stored one and both are 0." +
|
||||
"That is expected for messages which never got updated (mailbox msg).");
|
||||
return false;
|
||||
msg = "Sequence number is equal to the stored one and both are 0." +
|
||||
"That is expected for messages which never got updated (mailbox msg).";
|
||||
} else {
|
||||
log.debug("Sequence number is equal to the stored one. sequenceNumber = "
|
||||
+ newSequenceNumber + " / storedSequenceNumber=" + storedSequenceNumber);
|
||||
return false;
|
||||
msg = "Sequence number is equal to the stored one. sequenceNumber = "
|
||||
+ newSequenceNumber + " / storedSequenceNumber=" + storedSequenceNumber;
|
||||
}
|
||||
log.debug(msg);
|
||||
return false;
|
||||
} else {
|
||||
log.debug("Sequence number is invalid. sequenceNumber = "
|
||||
+ newSequenceNumber + " / storedSequenceNumber=" + storedSequenceNumber + "\n" +
|
||||
|
@ -450,7 +446,8 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
|||
boolean result = Sig.verify(ownerPubKey, hashOfDataAndSeqNr, signature);
|
||||
if (!result)
|
||||
log.error("Signature verification failed at checkSignature. " +
|
||||
"That should not happen. Consider it might be an attempt of fraud.");
|
||||
"That should not happen. ownerPubKey=" + ownerPubKey +
|
||||
", hashOfDataAndSeqNr=" + Arrays.toString(hashOfDataAndSeqNr) + ", signature=" + Arrays.toString(signature));
|
||||
|
||||
return result;
|
||||
} catch (CryptoException e) {
|
||||
|
@ -469,11 +466,13 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
|||
private boolean checkPublicKeys(ProtectedStorageEntry protectedStorageEntry, boolean isAddOperation) {
|
||||
boolean result;
|
||||
if (protectedStorageEntry.getStoragePayload() instanceof MailboxStoragePayload) {
|
||||
MailboxStoragePayload expirableMailboxStoragePayload = (MailboxStoragePayload) protectedStorageEntry.getStoragePayload();
|
||||
MailboxStoragePayload payload = (MailboxStoragePayload) protectedStorageEntry.getStoragePayload();
|
||||
if (isAddOperation)
|
||||
result = expirableMailboxStoragePayload.senderPubKeyForAddOperation.equals(protectedStorageEntry.ownerPubKey);
|
||||
result = payload.senderPubKeyForAddOperation != null &&
|
||||
payload.senderPubKeyForAddOperation.equals(protectedStorageEntry.ownerPubKey);
|
||||
else
|
||||
result = expirableMailboxStoragePayload.receiverPubKeyForRemoveOperation.equals(protectedStorageEntry.ownerPubKey);
|
||||
result = payload.receiverPubKeyForRemoveOperation != null &&
|
||||
payload.receiverPubKeyForRemoveOperation.equals(protectedStorageEntry.ownerPubKey);
|
||||
} else {
|
||||
// TODO We got sometimes a nullpointer at protectedStorageEntry.ownerPubKey
|
||||
// Probably caused by an exception at deserialization: Offer: Cannot be deserialized.null
|
||||
|
@ -482,16 +481,28 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
|||
protectedStorageEntry.ownerPubKey.equals(protectedStorageEntry.getStoragePayload().getOwnerPubKey());
|
||||
}
|
||||
|
||||
if (!result)
|
||||
log.error("PublicKey of payload data and ProtectedData are not matching. Consider it might be an attempt of fraud");
|
||||
if (!result) {
|
||||
String res1 = "null";
|
||||
String res2 = "null";
|
||||
if (protectedStorageEntry != null) {
|
||||
res1 = protectedStorageEntry.toString();
|
||||
if (protectedStorageEntry.getStoragePayload() != null && protectedStorageEntry.getStoragePayload().getOwnerPubKey() != null)
|
||||
res2 = protectedStorageEntry.getStoragePayload().getOwnerPubKey().toString();
|
||||
}
|
||||
|
||||
log.error("PublicKey of payload data and ProtectedData are not matching. protectedStorageEntry=" + res1 +
|
||||
"protectedStorageEntry.getStoragePayload().getOwnerPubKey()=" + res2);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean checkIfStoredDataPubKeyMatchesNewDataPubKey(PublicKey ownerPubKey, ByteArray hashOfData) {
|
||||
ProtectedStorageEntry storedData = map.get(hashOfData);
|
||||
boolean result = storedData.ownerPubKey.equals(ownerPubKey);
|
||||
boolean result = storedData.ownerPubKey != null && storedData.ownerPubKey.equals(ownerPubKey);
|
||||
if (!result)
|
||||
log.error("New data entry does not match our stored data. Consider it might be an attempt of fraud");
|
||||
log.error("New data entry does not match our stored data. storedData.ownerPubKey=" +
|
||||
(storedData.ownerPubKey != null ? storedData.ownerPubKey.toString() : "null") +
|
||||
", ownerPubKey=" + ownerPubKey);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -499,12 +510,13 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
|||
private boolean checkIfStoredMailboxDataMatchesNewMailboxData(PublicKey receiversPubKey, ByteArray hashOfData) {
|
||||
ProtectedStorageEntry storedData = map.get(hashOfData);
|
||||
if (storedData instanceof ProtectedMailboxStorageEntry) {
|
||||
ProtectedMailboxStorageEntry storedMailboxData = (ProtectedMailboxStorageEntry) storedData;
|
||||
ProtectedMailboxStorageEntry entry = (ProtectedMailboxStorageEntry) storedData;
|
||||
// publicKey is not the same (stored: sender, new: receiver)
|
||||
boolean result = storedMailboxData.receiversPubKey.equals(receiversPubKey)
|
||||
&& getHashAsByteArray(storedMailboxData.getStoragePayload()).equals(hashOfData);
|
||||
boolean result = entry.receiversPubKey.equals(receiversPubKey)
|
||||
&& getHashAsByteArray(entry.getStoragePayload()).equals(hashOfData);
|
||||
if (!result)
|
||||
log.error("New data entry does not match our stored data. Consider it might be an attempt of fraud");
|
||||
log.error("New data entry does not match our stored data. entry.receiversPubKey=" + entry.receiversPubKey
|
||||
+ ", receiversPubKey=" + receiversPubKey);
|
||||
|
||||
return result;
|
||||
} else {
|
||||
|
@ -533,6 +545,39 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
|||
return purged;
|
||||
}
|
||||
|
||||
private void printData(String info) {
|
||||
StringBuilder sb = new StringBuilder("\n\n------------------------------------------------------------\n");
|
||||
sb.append("Data set " + info + " operation");
|
||||
// We print the items sorted by hash with the payload class name and id
|
||||
List<Tuple2<String, ProtectedStorageEntry>> tempList = map.values().stream()
|
||||
.map(e -> new Tuple2<>(org.bitcoinj.core.Utils.HEX.encode(getHashAsByteArray(e.getStoragePayload()).bytes), e))
|
||||
.collect(Collectors.toList());
|
||||
tempList.sort((o1, o2) -> o1.first.compareTo(o2.first));
|
||||
tempList.stream().forEach(e -> {
|
||||
final ProtectedStorageEntry storageEntry = e.second;
|
||||
final StoragePayload storagePayload = storageEntry.getStoragePayload();
|
||||
final MapValue mapValue = sequenceNumberMap.get(getHashAsByteArray(storagePayload));
|
||||
sb.append("\n")
|
||||
.append("Hash=")
|
||||
.append(e.first)
|
||||
.append("; Class=")
|
||||
.append(storagePayload.getClass().getSimpleName())
|
||||
.append("; SequenceNumbers (Object/Stored)=")
|
||||
.append(storageEntry.sequenceNumber)
|
||||
.append(" / ")
|
||||
.append(mapValue != null ? mapValue.sequenceNr : "null")
|
||||
.append("; TimeStamp (Object/Stored)=")
|
||||
.append(storageEntry.creationTimeStamp)
|
||||
.append(" / ")
|
||||
.append(mapValue != null ? mapValue.timeStamp : "null")
|
||||
.append("; Payload=")
|
||||
.append(StringUtils.abbreviate(storagePayload.toString(), 100).replace("\n", ""));
|
||||
});
|
||||
sb.append("\n------------------------------------------------------------\n");
|
||||
log.debug(sb.toString());
|
||||
log.info("Data set " + info + " operation: size=" + map.values().size());
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Static class
|
||||
|
|
|
@ -4,6 +4,7 @@ import io.bitsquare.app.Version;
|
|||
import io.bitsquare.common.crypto.Sig;
|
||||
import io.bitsquare.p2p.NodeAddress;
|
||||
import io.bitsquare.p2p.messaging.PrefixedSealedAndSignedMessage;
|
||||
import io.bitsquare.p2p.peers.BroadcastHandler;
|
||||
import io.bitsquare.p2p.storage.storageentry.ProtectedStorageEntry;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -39,7 +40,7 @@ public final class MailboxStoragePayload implements StoragePayload {
|
|||
* senderStoragePublicKey has to be equal to the ownerPubKey of the ProtectedData
|
||||
*
|
||||
* @see ProtectedStorageEntry#ownerPubKey
|
||||
* @see io.bitsquare.p2p.storage.P2PDataStorage#add(ProtectedStorageEntry, NodeAddress)
|
||||
* @see io.bitsquare.p2p.storage.P2PDataStorage#add(ProtectedStorageEntry, NodeAddress, BroadcastHandler.Listener, boolean)
|
||||
*/
|
||||
public transient PublicKey senderPubKeyForAddOperation;
|
||||
private final byte[] senderPubKeyForAddOperationBytes;
|
||||
|
@ -48,7 +49,7 @@ public final class MailboxStoragePayload implements StoragePayload {
|
|||
* senderStoragePublicKey has to be equal to the ownerPubKey of the ProtectedData
|
||||
*
|
||||
* @see ProtectedStorageEntry#ownerPubKey
|
||||
* @see io.bitsquare.p2p.storage.P2PDataStorage#remove(ProtectedStorageEntry, NodeAddress)
|
||||
* @see io.bitsquare.p2p.storage.P2PDataStorage#remove(ProtectedStorageEntry, NodeAddress, boolean)
|
||||
*/
|
||||
public transient PublicKey receiverPubKeyForRemoveOperation;
|
||||
private final byte[] receiverPubKeyForRemoveOperationBytes;
|
||||
|
@ -69,7 +70,7 @@ public final class MailboxStoragePayload implements StoragePayload {
|
|||
senderPubKeyForAddOperation = KeyFactory.getInstance(Sig.KEY_ALGO, "BC").generatePublic(new X509EncodedKeySpec(senderPubKeyForAddOperationBytes));
|
||||
receiverPubKeyForRemoveOperation = KeyFactory.getInstance(Sig.KEY_ALGO, "BC").generatePublic(new X509EncodedKeySpec(receiverPubKeyForRemoveOperationBytes));
|
||||
} catch (Throwable t) {
|
||||
log.warn("Exception at readObject: " + t.getMessage());
|
||||
log.warn("Exception at readObject: " + t.getMessage() + "\nThis= " + this.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,5 +12,4 @@
|
|||
|
||||
<logger name="com.msopentech.thali.toronionproxy.OnionProxyManagerEventHandler" level="WARN"/>
|
||||
|
||||
|
||||
</configuration>
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
package io.bitsquare.p2p.seed;
|
||||
package io.bitsquare.p2p;
|
||||
|
||||
import ch.qos.logback.classic.Level;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import io.bitsquare.app.Log;
|
||||
import io.bitsquare.app.Version;
|
||||
import io.bitsquare.common.Clock;
|
||||
import io.bitsquare.common.CommonOptionKeys;
|
||||
import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.common.util.Utilities;
|
||||
import io.bitsquare.network.OptionKeys;
|
||||
import io.bitsquare.p2p.NodeAddress;
|
||||
import io.bitsquare.p2p.P2PService;
|
||||
import io.bitsquare.p2p.P2PServiceListener;
|
||||
import io.bitsquare.network.NetworkOptionKeys;
|
||||
import io.bitsquare.p2p.peers.BanList;
|
||||
import io.bitsquare.p2p.seed.SeedNodesRepository;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -26,11 +25,13 @@ import java.util.Set;
|
|||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
public class SeedNode {
|
||||
private static final Logger log = LoggerFactory.getLogger(SeedNode.class);
|
||||
// Previously used seednode class, replaced now by bootstrap module. We keep it here as it was used in tests...
|
||||
public class DummySeedNode {
|
||||
private static final Logger log = LoggerFactory.getLogger(DummySeedNode.class);
|
||||
public static final int MAX_CONNECTIONS_LIMIT = 1000;
|
||||
public static final int MAX_CONNECTIONS_DEFAULT = 50;
|
||||
|
||||
public static final String SEED_NODES_LIST = "seedNodes";
|
||||
public static final String HELP = "help";
|
||||
private NodeAddress mySeedNodeAddress = new NodeAddress("localhost:8001");
|
||||
private int maxConnections = MAX_CONNECTIONS_DEFAULT; // we keep default a higher connection size for seed nodes
|
||||
private boolean useLocalhost = false;
|
||||
|
@ -40,7 +41,7 @@ public class SeedNode {
|
|||
private final String defaultUserDataDir;
|
||||
private Level logLevel = Level.WARN;
|
||||
|
||||
public SeedNode(String defaultUserDataDir) {
|
||||
public DummySeedNode(String defaultUserDataDir) {
|
||||
Log.traceCall("defaultUserDataDir=" + defaultUserDataDir);
|
||||
this.defaultUserDataDir = defaultUserDataDir;
|
||||
}
|
||||
|
@ -77,34 +78,34 @@ public class SeedNode {
|
|||
String arg = args[i];
|
||||
if (arg.startsWith("--"))
|
||||
arg = arg.substring(2);
|
||||
if (arg.startsWith(OptionKeys.MY_ADDRESS)) {
|
||||
arg = arg.substring(OptionKeys.MY_ADDRESS.length() + 1);
|
||||
if (arg.startsWith(NetworkOptionKeys.MY_ADDRESS)) {
|
||||
arg = arg.substring(NetworkOptionKeys.MY_ADDRESS.length() + 1);
|
||||
checkArgument(arg.contains(":") && arg.split(":").length == 2 && arg.split(":")[1].length() > 3, "Wrong program argument: " + arg);
|
||||
mySeedNodeAddress = new NodeAddress(arg);
|
||||
log.info("From processArgs: mySeedNodeAddress=" + mySeedNodeAddress);
|
||||
} else if (arg.startsWith(OptionKeys.NETWORK_ID)) {
|
||||
arg = arg.substring(OptionKeys.NETWORK_ID.length() + 1);
|
||||
} else if (arg.startsWith(NetworkOptionKeys.NETWORK_ID)) {
|
||||
arg = arg.substring(NetworkOptionKeys.NETWORK_ID.length() + 1);
|
||||
networkId = Integer.parseInt(arg);
|
||||
log.info("From processArgs: networkId=" + networkId);
|
||||
checkArgument(networkId > -1 && networkId < 3,
|
||||
"networkId out of scope (Mainnet = 0, TestNet = 1, Regtest = 2)");
|
||||
Version.setBtcNetworkId(networkId);
|
||||
} else if (arg.startsWith(OptionKeys.MAX_CONNECTIONS)) {
|
||||
arg = arg.substring(OptionKeys.MAX_CONNECTIONS.length() + 1);
|
||||
} else if (arg.startsWith(NetworkOptionKeys.MAX_CONNECTIONS)) {
|
||||
arg = arg.substring(NetworkOptionKeys.MAX_CONNECTIONS.length() + 1);
|
||||
maxConnections = Integer.parseInt(arg);
|
||||
log.info("From processArgs: maxConnections=" + maxConnections);
|
||||
checkArgument(maxConnections < MAX_CONNECTIONS_LIMIT, "maxConnections seems to be a bit too high...");
|
||||
} else if (arg.startsWith(OptionKeys.USE_LOCALHOST)) {
|
||||
arg = arg.substring(OptionKeys.USE_LOCALHOST.length() + 1);
|
||||
} else if (arg.startsWith(NetworkOptionKeys.USE_LOCALHOST)) {
|
||||
arg = arg.substring(NetworkOptionKeys.USE_LOCALHOST.length() + 1);
|
||||
checkArgument(arg.equals("true") || arg.equals("false"));
|
||||
useLocalhost = ("true").equals(arg);
|
||||
log.info("From processArgs: useLocalhost=" + useLocalhost);
|
||||
} else if (arg.startsWith(io.bitsquare.common.OptionKeys.LOG_LEVEL_KEY)) {
|
||||
arg = arg.substring(io.bitsquare.common.OptionKeys.LOG_LEVEL_KEY.length() + 1);
|
||||
} else if (arg.startsWith(CommonOptionKeys.LOG_LEVEL_KEY)) {
|
||||
arg = arg.substring(CommonOptionKeys.LOG_LEVEL_KEY.length() + 1);
|
||||
logLevel = Level.toLevel(arg.toUpperCase());
|
||||
log.info("From processArgs: logLevel=" + logLevel);
|
||||
} else if (arg.startsWith(OptionKeys.SEED_NODES_LIST)) {
|
||||
arg = arg.substring(OptionKeys.SEED_NODES_LIST.length() + 1);
|
||||
} else if (arg.startsWith(SEED_NODES_LIST)) {
|
||||
arg = arg.substring(SEED_NODES_LIST.length() + 1);
|
||||
checkArgument(arg.contains(":") && arg.split(":").length > 1 && arg.split(":")[1].length() > 3,
|
||||
"Wrong program argument " + arg);
|
||||
List<String> list = Arrays.asList(arg.split(","));
|
||||
|
@ -116,8 +117,8 @@ public class SeedNode {
|
|||
});
|
||||
log.info("From processArgs: progArgSeedNodes=" + progArgSeedNodes);
|
||||
progArgSeedNodes.remove(mySeedNodeAddress);
|
||||
} else if (arg.startsWith(OptionKeys.BAN_LIST)) {
|
||||
arg = arg.substring(OptionKeys.BAN_LIST.length() + 1);
|
||||
} else if (arg.startsWith(NetworkOptionKeys.BAN_LIST)) {
|
||||
arg = arg.substring(NetworkOptionKeys.BAN_LIST.length() + 1);
|
||||
checkArgument(arg.contains(":") && arg.split(":").length > 1 && arg.split(":")[1].length() > 3,
|
||||
"Wrong program argument " + arg);
|
||||
List<String> list = Arrays.asList(arg.split(","));
|
||||
|
@ -127,7 +128,7 @@ public class SeedNode {
|
|||
BanList.add(new NodeAddress(e));
|
||||
});
|
||||
log.info("From processArgs: ignoreList=" + list);
|
||||
} else if (arg.startsWith(OptionKeys.HELP)) {
|
||||
} else if (arg.startsWith(HELP)) {
|
||||
log.info(USAGE);
|
||||
} else {
|
||||
log.error("Invalid argument. " + arg + "\n" + USAGE);
|
||||
|
@ -186,7 +187,7 @@ public class SeedNode {
|
|||
|
||||
seedNodesRepository.setNodeAddressToExclude(mySeedNodeAddress);
|
||||
seedNodeP2PService = new P2PService(seedNodesRepository, mySeedNodeAddress.port, maxConnections,
|
||||
torDir, useLocalhost, networkId, storageDir, null, new Clock(), null, null);
|
||||
torDir, useLocalhost, networkId, storageDir, null, null, null, new Clock(), null, null);
|
||||
seedNodeP2PService.start(false, listener);
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package io.bitsquare.p2p;
|
||||
|
||||
import io.bitsquare.p2p.network.LocalhostNetworkNode;
|
||||
import io.bitsquare.p2p.seed.SeedNode;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
@ -27,9 +26,9 @@ public class PeerServiceTest {
|
|||
final boolean useLocalhost = true;
|
||||
private CountDownLatch latch;
|
||||
private int sleepTime;
|
||||
private SeedNode seedNode1, seedNode2, seedNode3;
|
||||
private DummySeedNode seedNode1, seedNode2, seedNode3;
|
||||
private Set<NodeAddress> seedNodeAddresses = new HashSet<>();
|
||||
private List<SeedNode> seedNodes = new ArrayList<>();
|
||||
private List<DummySeedNode> seedNodes = new ArrayList<>();
|
||||
private String test_dummy_dir = "test_dummy_dir";
|
||||
;
|
||||
|
||||
|
@ -81,7 +80,7 @@ public class PeerServiceTest {
|
|||
int port = 8000 + i;
|
||||
NodeAddress nodeAddress = new NodeAddress("localhost:" + port);
|
||||
seedNodeAddresses.add(nodeAddress);
|
||||
SeedNode seedNode = new SeedNode(test_dummy_dir);
|
||||
DummySeedNode seedNode = new DummySeedNode(test_dummy_dir);
|
||||
seedNodes.add(seedNode);
|
||||
seedNode.createAndStartP2PService(true);
|
||||
|
||||
|
@ -180,7 +179,7 @@ public class PeerServiceTest {
|
|||
|
||||
latch = new CountDownLatch(6);
|
||||
|
||||
seedNode1 = new SeedNode("test_dummy_dir");
|
||||
seedNode1 = new DummySeedNode("test_dummy_dir");
|
||||
seedNode1.createAndStartP2PService(nodeAddress1, MAX_CONNECTIONS, useLocalhost, 2, true, seedNodeAddresses, new P2PServiceListener() {
|
||||
@Override
|
||||
public void onRequestingDataCompleted() {
|
||||
|
@ -225,7 +224,7 @@ public class PeerServiceTest {
|
|||
|
||||
Thread.sleep(500);
|
||||
|
||||
seedNode2 = new SeedNode("test_dummy_dir");
|
||||
seedNode2 = new DummySeedNode("test_dummy_dir");
|
||||
seedNode2.createAndStartP2PService(nodeAddress2, MAX_CONNECTIONS, useLocalhost, 2, true, seedNodeAddresses, new P2PServiceListener() {
|
||||
@Override
|
||||
public void onRequestingDataCompleted() {
|
||||
|
@ -277,12 +276,12 @@ public class PeerServiceTest {
|
|||
log.debug("### start");
|
||||
LocalhostNetworkNode.setSimulateTorDelayTorNode(0);
|
||||
LocalhostNetworkNode.setSimulateTorDelayHiddenService(0);
|
||||
SeedNode seedNode1 = getAndStartSeedNode(8001);
|
||||
DummySeedNode seedNode1 = getAndStartSeedNode(8001);
|
||||
log.debug("### seedNode1");
|
||||
Thread.sleep(100);
|
||||
log.debug("### seedNode1 100");
|
||||
Thread.sleep(1000);
|
||||
SeedNode seedNode2 = getAndStartSeedNode(8002);
|
||||
DummySeedNode seedNode2 = getAndStartSeedNode(8002);
|
||||
|
||||
// authentication:
|
||||
// node2 -> node1 RequestAuthenticationMessage
|
||||
|
@ -466,8 +465,8 @@ public class PeerServiceTest {
|
|||
shutDownLatch.await();*/
|
||||
}
|
||||
|
||||
private SeedNode getAndStartSeedNode(int port) throws InterruptedException {
|
||||
SeedNode seedNode = new SeedNode("test_dummy_dir");
|
||||
private DummySeedNode getAndStartSeedNode(int port) throws InterruptedException {
|
||||
DummySeedNode seedNode = new DummySeedNode("test_dummy_dir");
|
||||
|
||||
latch = new CountDownLatch(1);
|
||||
seedNode.createAndStartP2PService(new NodeAddress("localhost", port), MAX_CONNECTIONS, useLocalhost, 2, true, seedNodeAddresses, new P2PServiceListener() {
|
||||
|
|
|
@ -3,7 +3,6 @@ package io.bitsquare.p2p;
|
|||
import io.bitsquare.common.Clock;
|
||||
import io.bitsquare.common.crypto.KeyRing;
|
||||
import io.bitsquare.crypto.EncryptionService;
|
||||
import io.bitsquare.p2p.seed.SeedNode;
|
||||
import io.bitsquare.p2p.seed.SeedNodesRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -64,25 +63,25 @@ public class TestUtils {
|
|||
return result;
|
||||
}
|
||||
|
||||
public static SeedNode getAndStartSeedNode(int port, boolean useLocalhost, Set<NodeAddress> seedNodes) throws InterruptedException {
|
||||
SeedNode seedNode;
|
||||
public static DummySeedNode getAndStartSeedNode(int port, boolean useLocalhost, Set<NodeAddress> seedNodes) throws InterruptedException {
|
||||
DummySeedNode seedNode;
|
||||
|
||||
if (useLocalhost) {
|
||||
seedNodes.add(new NodeAddress("localhost:8001"));
|
||||
seedNodes.add(new NodeAddress("localhost:8002"));
|
||||
seedNodes.add(new NodeAddress("localhost:8003"));
|
||||
sleepTime = 100;
|
||||
seedNode = new SeedNode(test_dummy_dir);
|
||||
seedNode = new DummySeedNode(test_dummy_dir);
|
||||
} else {
|
||||
seedNodes.add(new NodeAddress("3omjuxn7z73pxoee.onion:8001"));
|
||||
seedNodes.add(new NodeAddress("j24fxqyghjetgpdx.onion:8002"));
|
||||
seedNodes.add(new NodeAddress("45367tl6unwec6kw.onion:8003"));
|
||||
sleepTime = 10000;
|
||||
seedNode = new SeedNode(test_dummy_dir);
|
||||
seedNode = new DummySeedNode(test_dummy_dir);
|
||||
}
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
seedNode.createAndStartP2PService(new NodeAddress("localhost", port), SeedNode.MAX_CONNECTIONS_DEFAULT, useLocalhost, 2, true,
|
||||
seedNode.createAndStartP2PService(new NodeAddress("localhost", port), DummySeedNode.MAX_CONNECTIONS_DEFAULT, useLocalhost, 2, true,
|
||||
seedNodes, new P2PServiceListener() {
|
||||
@Override
|
||||
public void onRequestingDataCompleted() {
|
||||
|
@ -139,7 +138,7 @@ public class TestUtils {
|
|||
}
|
||||
|
||||
P2PService p2PService = new P2PService(seedNodesRepository, port, new File("seed_node_" + port), useLocalhost,
|
||||
2, P2PService.MAX_CONNECTIONS_DEFAULT, new File("dummy"), null, new Clock(), encryptionService, keyRing);
|
||||
2, P2PService.MAX_CONNECTIONS_DEFAULT, new File("dummy"), null, null, null, new Clock(), encryptionService, keyRing);
|
||||
p2PService.start(false, new P2PServiceListener() {
|
||||
@Override
|
||||
public void onRequestingDataCompleted() {
|
||||
|
|
|
@ -9,12 +9,8 @@ import io.bitsquare.common.crypto.PubKeyRing;
|
|||
import io.bitsquare.common.util.Tuple3;
|
||||
import io.bitsquare.crypto.DecryptedMsgWithPubKey;
|
||||
import io.bitsquare.crypto.EncryptionService;
|
||||
import io.bitsquare.p2p.NodeAddress;
|
||||
import io.bitsquare.p2p.P2PService;
|
||||
import io.bitsquare.p2p.P2PServiceListener;
|
||||
import io.bitsquare.p2p.Utils;
|
||||
import io.bitsquare.p2p.*;
|
||||
import io.bitsquare.p2p.messaging.*;
|
||||
import io.bitsquare.p2p.seed.SeedNode;
|
||||
import io.bitsquare.p2p.seed.SeedNodesRepository;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
|
@ -140,7 +136,7 @@ public class NetworkStressTest {
|
|||
/**
|
||||
* A single seed node that other nodes will contact to request initial data.
|
||||
*/
|
||||
private SeedNode seedNode;
|
||||
private DummySeedNode seedNode;
|
||||
/**
|
||||
* The repository of seed nodes used in the test.
|
||||
*/
|
||||
|
@ -271,12 +267,12 @@ public class NetworkStressTest {
|
|||
UserThread.setExecutor(Executors.newSingleThreadExecutor());
|
||||
|
||||
// Create and start the seed node.
|
||||
seedNode = new SeedNode(testDataDir.toString());
|
||||
seedNode = new DummySeedNode(testDataDir.toString());
|
||||
final NodeAddress seedNodeAddress = newSeedNodeAddress();
|
||||
useLocalhost = seedNodeAddress.hostName.equals("localhost");
|
||||
final Set<NodeAddress> seedNodes = new HashSet<>(1);
|
||||
seedNodes.add(seedNodeAddress); // the only seed node in tests
|
||||
seedNode.createAndStartP2PService(seedNodeAddress, SeedNode.MAX_CONNECTIONS_DEFAULT, useLocalhost,
|
||||
seedNode.createAndStartP2PService(seedNodeAddress, DummySeedNode.MAX_CONNECTIONS_DEFAULT, useLocalhost,
|
||||
REGTEST_NETWORK_ID, USE_DETAILED_LOGGING, seedNodes,
|
||||
new SeedServiceListener(localServicesLatch, localServicesFailed));
|
||||
print("created seed node");
|
||||
|
@ -381,7 +377,7 @@ public class NetworkStressTest {
|
|||
final EncryptionService peerEncryptionService = new EncryptionService(peerKeyRing);
|
||||
|
||||
return new P2PService(seedNodesRepository, port, peerTorDir, useLocalhost,
|
||||
REGTEST_NETWORK_ID, P2PService.MAX_CONNECTIONS_DEFAULT, peerStorageDir, null, new Clock(), peerEncryptionService, peerKeyRing);
|
||||
REGTEST_NETWORK_ID, P2PService.MAX_CONNECTIONS_DEFAULT, peerStorageDir, null, null, null, new Clock(), peerEncryptionService, peerKeyRing);
|
||||
}
|
||||
|
||||
// ## TEST SETUP: P2P service listener classes
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package io.bitsquare.p2p.routing;
|
||||
|
||||
import io.bitsquare.p2p.DummySeedNode;
|
||||
import io.bitsquare.p2p.NodeAddress;
|
||||
import io.bitsquare.p2p.P2PService;
|
||||
import io.bitsquare.p2p.P2PServiceListener;
|
||||
import io.bitsquare.p2p.network.LocalhostNetworkNode;
|
||||
import io.bitsquare.p2p.seed.SeedNode;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
|
@ -31,7 +31,7 @@ public class PeerManagerTest {
|
|||
private CountDownLatch latch;
|
||||
private Set<NodeAddress> seedNodes;
|
||||
private int sleepTime;
|
||||
private SeedNode seedNode1, seedNode2, seedNode3;
|
||||
private DummySeedNode seedNode1, seedNode2, seedNode3;
|
||||
|
||||
@Before
|
||||
public void setup() throws InterruptedException {
|
||||
|
@ -81,7 +81,7 @@ public class PeerManagerTest {
|
|||
seedNodes = new HashSet<>();
|
||||
NodeAddress nodeAddress = new NodeAddress("localhost:8001");
|
||||
seedNodes.add(nodeAddress);
|
||||
seedNode1 = new SeedNode("test_dummy_dir");
|
||||
seedNode1 = new DummySeedNode("test_dummy_dir");
|
||||
latch = new CountDownLatch(2);
|
||||
seedNode1.createAndStartP2PService(nodeAddress, MAX_CONNECTIONS, useLocalhost, 2, true,
|
||||
seedNodes, new P2PServiceListener() {
|
||||
|
@ -142,7 +142,7 @@ public class PeerManagerTest {
|
|||
|
||||
latch = new CountDownLatch(6);
|
||||
|
||||
seedNode1 = new SeedNode("test_dummy_dir");
|
||||
seedNode1 = new DummySeedNode("test_dummy_dir");
|
||||
seedNode1.createAndStartP2PService(nodeAddress1, MAX_CONNECTIONS, useLocalhost, 2, true, seedNodes, new P2PServiceListener() {
|
||||
@Override
|
||||
public void onRequestingDataCompleted() {
|
||||
|
@ -187,7 +187,7 @@ public class PeerManagerTest {
|
|||
|
||||
Thread.sleep(500);
|
||||
|
||||
seedNode2 = new SeedNode("test_dummy_dir");
|
||||
seedNode2 = new DummySeedNode("test_dummy_dir");
|
||||
seedNode2.createAndStartP2PService(nodeAddress2, MAX_CONNECTIONS, useLocalhost, 2, true, seedNodes, new P2PServiceListener() {
|
||||
@Override
|
||||
public void onRequestingDataCompleted() {
|
||||
|
@ -239,12 +239,12 @@ public class PeerManagerTest {
|
|||
log.debug("### start");
|
||||
LocalhostNetworkNode.setSimulateTorDelayTorNode(0);
|
||||
LocalhostNetworkNode.setSimulateTorDelayHiddenService(0);
|
||||
SeedNode seedNode1 = getAndStartSeedNode(8001);
|
||||
DummySeedNode seedNode1 = getAndStartSeedNode(8001);
|
||||
log.debug("### seedNode1");
|
||||
Thread.sleep(100);
|
||||
log.debug("### seedNode1 100");
|
||||
Thread.sleep(1000);
|
||||
SeedNode seedNode2 = getAndStartSeedNode(8002);
|
||||
DummySeedNode seedNode2 = getAndStartSeedNode(8002);
|
||||
|
||||
// authentication:
|
||||
// node2 -> node1 RequestAuthenticationMessage
|
||||
|
@ -428,8 +428,8 @@ public class PeerManagerTest {
|
|||
shutDownLatch.await();*/
|
||||
}
|
||||
|
||||
private SeedNode getAndStartSeedNode(int port) throws InterruptedException {
|
||||
SeedNode seedNode = new SeedNode("test_dummy_dir");
|
||||
private DummySeedNode getAndStartSeedNode(int port) throws InterruptedException {
|
||||
DummySeedNode seedNode = new DummySeedNode("test_dummy_dir");
|
||||
|
||||
latch = new CountDownLatch(1);
|
||||
seedNode.createAndStartP2PService(new NodeAddress("localhost", port), MAX_CONNECTIONS, useLocalhost, 2, true, seedNodes, new P2PServiceListener() {
|
||||
|
|
|
@ -15,15 +15,14 @@ cp gui/target/shaded.jar "/Users/mk/vm_shared_windows/Bitsquare-$version.jar"
|
|||
cp gui/target/shaded.jar "/Users/mk/vm_shared_ubuntu14_32bit/Bitsquare-$version.jar"
|
||||
cp gui/target/shaded.jar "/Users/mk/vm_shared_windows_32bit/Bitsquare-$version.jar"
|
||||
|
||||
cp seednode/target/SeedNode.jar "gui/deploy/SeedNode.jar"
|
||||
cp seednode/target/SeedNode.jar "gui/deploy/SeedNode-$version.jar"
|
||||
|
||||
cp jdkfix/target/jdkfix-$version.jar "/Users/mk/vm_shared_ubuntu/jdkfix-$version.jar"
|
||||
cp jdkfix/target/jdkfix-$version.jar "/Users/mk/vm_shared_windows/jdkfix-$version.jar"
|
||||
cp jdkfix/target/jdkfix-$version.jar "/Users/mk/vm_shared_ubuntu14_32bit/jdkfix-$version.jar"
|
||||
cp jdkfix/target/jdkfix-$version.jar "/Users/mk/vm_shared_windows_32bit/jdkfix-$version.jar"
|
||||
exit
|
||||
|
||||
echo "Using $JAVA_HOME"
|
||||
echo "Using JAVA_HOME: $JAVA_HOME"
|
||||
$JAVA_HOME/bin/javapackager \
|
||||
-deploy \
|
||||
-BjvmOptions=-Xbootclasspath/a:"jdkfix-$version.jar":"../PlugIns/Java.runtime/Contents/Home/jre/lib/ext/jfxrt.jar" \
|
||||
|
@ -48,6 +47,4 @@ rm "gui/deploy/Bitsquare.jnlp"
|
|||
mv "gui/deploy/bundles/Bitsquare-$version.dmg" "gui/deploy/Bitsquare-$version.dmg"
|
||||
rm -r "gui/deploy/bundles"
|
||||
|
||||
mv "gui/deploy/SeedNode.jar" "gui/deploy/SeedNode-$version.jar"
|
||||
|
||||
cd package/mac
|
|
@ -5,6 +5,8 @@ version="0.4.9"
|
|||
target_dir="/Users/mk/Documents/__bitsquare/_releases/$version"
|
||||
src_dir="/Users/mk/Documents/_intellij/bitsquare"
|
||||
|
||||
mkdir -p $target_dir
|
||||
|
||||
mac="Bitsquare-$version.dmg"
|
||||
cp "$src_dir/gui/deploy/$mac" "$target_dir/"
|
||||
cp "$src_dir/gui/deploy/SeedNode-$version.jar" "$target_dir/"
|
||||
|
@ -15,7 +17,7 @@ cp "/Users/mk/vm_shared_ubuntu14_32bit/$deb32" "$target_dir/"
|
|||
deb64="Bitsquare-64bit-$version.deb"
|
||||
cp "/Users/mk/vm_shared_ubuntu/$deb64" "$target_dir/"
|
||||
|
||||
exe="Bitsquare.exe"
|
||||
exe="Bitsquare-$version.exe"
|
||||
win32="Bitsquare-32bit-$version.exe"
|
||||
cp "/Users/mk/vm_shared_windows_32bit/bundles/$exe" "$target_dir/$win32"
|
||||
win64="Bitsquare-64bit-$version.exe"
|
||||
|
|
24
package/windows/32bitBuild.bat
Normal file
24
package/windows/32bitBuild.bat
Normal file
|
@ -0,0 +1,24 @@
|
|||
:: edit iss file -> AppVersion
|
||||
|
||||
:: Copy gui/deploy.Bitsquare.jar file from mac build to windows
|
||||
:: edit -> -BappVersion and -srcfiles
|
||||
|
||||
:: 64 bit build
|
||||
:: Needs Inno Setup 5 or later (http://www.jrsoftware.org/isdl.php)
|
||||
|
||||
SET version=0.4.9
|
||||
SET jdk=C:\Program Files\Java\jdk1.8.0_92
|
||||
SET outdir=\\VBOXSVR\vm_shared_windows_32bit
|
||||
|
||||
call "%jdk%\bin\javapackager.exe" -deploy ^
|
||||
-BappVersion="%version%" ^
|
||||
-native exe ^
|
||||
-name Bitsquare ^
|
||||
-title Bitsquare ^
|
||||
-vendor Bitsquare ^
|
||||
-outdir %outdir% ^
|
||||
-appclass io.bitsquare.app.BitsquareAppMain ^
|
||||
-srcfiles %outdir%\Bitsquare-%version%.jar ^
|
||||
-outfile Bitsquare ^
|
||||
-Bruntime="%jdk%\jre" ^
|
||||
-BjvmProperties=-Djava.net.preferIPv4Stack=true
|
|
@ -1,4 +1,4 @@
|
|||
:: edit iss file -> AppVersion
|
||||
:: edit iss file -> AppVersion
|
||||
|
||||
:: Copy gui/deploy.Bitsquare.jar file from mac build to windows
|
||||
:: edit -> -BappVersion and -srcfiles
|
||||
|
@ -6,12 +6,13 @@
|
|||
:: 64 bit build
|
||||
:: Needs Inno Setup 5 or later (http://www.jrsoftware.org/isdl.php)
|
||||
|
||||
:: Did not get -BjvmOptions=-Xbootclasspath working on windows, but if the jdkfix jar is copied into the jdk/jre dir it will override the default classes
|
||||
|
||||
SET version=0.4.9
|
||||
SET jdk=C:\Program Files\Java\jdk1.8.0_92
|
||||
SET outdir=\\VBOXSVR\vm_shared_windows
|
||||
|
||||
call "%jdk%\bin\javapackager.exe" -deploy ^
|
||||
-BjvmOptions=-Xbootclasspath/a:^"jdkfix-0.4.9.jar^";^"..\runtime\lib\ext\jfxrt.jar^" ^
|
||||
-BappVersion="%version%" ^
|
||||
-native exe ^
|
||||
-name Bitsquare ^
|
||||
|
@ -19,12 +20,7 @@ call "%jdk%\bin\javapackager.exe" -deploy ^
|
|||
-vendor Bitsquare ^
|
||||
-outdir %outdir% ^
|
||||
-appclass io.bitsquare.app.BitsquareAppMain ^
|
||||
-srcfiles "%outdir%\Bitsquare-%version%.jar;%outdir%\jdkfix-%version%.jar" ^
|
||||
-srcfiles %outdir%\Bitsquare-%version%.jar ^
|
||||
-outfile Bitsquare ^
|
||||
-Bruntime="%jdk%\jre" ^
|
||||
-BjvmProperties=-Djava.net.preferIPv4Stack=true
|
||||
|
||||
:: -BjvmOptions=-verbose:class
|
||||
:: those works
|
||||
:: java -Xbootclasspath/a:^"jdkfix-0.4.9.jar^";^"..\runtime\lib\ext\jfxrt.jar^" -jar Bitsquare-0.4.9.jar
|
||||
:: java -Xbootclasspath/a:"jdkfix-0.4.9.jar";"..\runtime\lib\ext\jfxrt.jar" -jar Bitsquare-0.4.9.jar
|
||||
-BjvmProperties=-Djava.net.preferIPv4Stack=true
|
|
@ -1,13 +0,0 @@
|
|||
cd ..\..\
|
||||
mkdir gui\deploy
|
||||
|
||||
:: edit iss file -> AppVersion
|
||||
|
||||
:: Copy gui/deploy.Bitsquare.jar file from mac build to windows
|
||||
:: edit -> -BappVersion and -srcfiles
|
||||
|
||||
:: 32 bit build
|
||||
:: Needs Inno Setup 5 or later (http://www.jrsoftware.org/isdl.php)
|
||||
call "C:\Program Files\Java\jdk1.8.0_92\bin\javapackager.exe" -deploy -BappVersion=0.4.9 -native exe -name Bitsquare -title Bitsquare -vendor Bitsquare -outdir "\\VBOXSVR\vm_shared_windows_32bit" -appclass io.bitsquare.app.BitsquareAppMain -srcfiles "\\VBOXSVR\vm_shared_windows_32bit\Bitsquare-0.4.9.jar" -outfile Bitsquare -Bruntime="C:\Program Files\Java\jdk1.8.0_92\jre" -BjvmProperties=-Djava.net.preferIPv4Stack=true
|
||||
|
||||
cd package\windows
|
4
pom.xml
4
pom.xml
|
@ -44,8 +44,10 @@
|
|||
<module>jtorctl</module>
|
||||
<module>jtorproxy</module>
|
||||
<module>network</module>
|
||||
<module>seednode</module>
|
||||
<module>gui</module>
|
||||
<module>headless</module>
|
||||
<module>seednode</module>
|
||||
<module>monitor</module>
|
||||
<module>jdkfix</module>
|
||||
</modules>
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue