From 4da9e1848e6c836006f2a2486af2a3691f605d86 Mon Sep 17 00:00:00 2001 From: Mike Rosseel Date: Fri, 30 Jun 2017 21:15:30 +0200 Subject: [PATCH] rebase of downloadUpdates branch by Simon Tuberlin on the Development HEAD - rebase conflicts due to package renames - some bouncy castle pgp stuff changed with new releases --- .../common/util/DownloadType.java | 2 +- .../common/util/DownloadUtil.java | 16 +- .../java/io/bisq/common/util/Utilities.java | 27 + .../io/bitsquare/common/util/Utilities.java | 494 -------- .../java/io/bitsquare/alert/AlertManager.java | 152 --- .../arbitration/ArbitratorManager.java | 299 ----- .../java/io/bisq/gui/main/MainViewModel.java | 10 +- .../windows/DisplayUpdateDownloadWindow.java | 29 +- .../io/bitsquare/gui/main/MainViewModel.java | 1044 ----------------- pom.xml | 2 +- 10 files changed, 59 insertions(+), 2016 deletions(-) rename common/src/main/java/io/{bitsquare => bisq}/common/util/DownloadType.java (66%) rename common/src/main/java/io/{bitsquare => bisq}/common/util/DownloadUtil.java (95%) delete mode 100644 common/src/main/java/io/bitsquare/common/util/Utilities.java delete mode 100644 core/src/main/java/io/bitsquare/alert/AlertManager.java delete mode 100644 core/src/main/java/io/bitsquare/arbitration/ArbitratorManager.java rename gui/src/main/java/io/{bitsquare => bisq}/gui/main/overlays/windows/DisplayUpdateDownloadWindow.java (94%) delete mode 100644 gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java diff --git a/common/src/main/java/io/bitsquare/common/util/DownloadType.java b/common/src/main/java/io/bisq/common/util/DownloadType.java similarity index 66% rename from common/src/main/java/io/bitsquare/common/util/DownloadType.java rename to common/src/main/java/io/bisq/common/util/DownloadType.java index 618e18a530..945aee2646 100644 --- a/common/src/main/java/io/bitsquare/common/util/DownloadType.java +++ b/common/src/main/java/io/bisq/common/util/DownloadType.java @@ -1,4 +1,4 @@ -package io.bitsquare.common.util; +package io.bisq.common.util; public enum DownloadType { INST, diff --git a/common/src/main/java/io/bitsquare/common/util/DownloadUtil.java b/common/src/main/java/io/bisq/common/util/DownloadUtil.java similarity index 95% rename from common/src/main/java/io/bitsquare/common/util/DownloadUtil.java rename to common/src/main/java/io/bisq/common/util/DownloadUtil.java index cd62649f30..214a8d63f2 100644 --- a/common/src/main/java/io/bitsquare/common/util/DownloadUtil.java +++ b/common/src/main/java/io/bisq/common/util/DownloadUtil.java @@ -1,4 +1,4 @@ -package io.bitsquare.common.util; +package io.bisq.common.util; /** * A utility that downloads a file from a URL. @@ -8,8 +8,10 @@ package io.bitsquare.common.util; import javafx.concurrent.Task; -import org.bouncycastle.openpgp.*; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openpgp.*; +import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; +import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; import java.io.*; import java.net.HttpURLConnection; @@ -153,7 +155,7 @@ public class DownloadUtil extends Task { // Read keys from file is = PGPUtil.getDecoderStream(new FileInputStream(pubKeyFile)); - PGPPublicKeyRingCollection publicKeyRingCollection = new PGPPublicKeyRingCollection(is); + PGPPublicKeyRingCollection publicKeyRingCollection = new PGPPublicKeyRingCollection(is, new JcaKeyFingerprintCalculator()); is.close(); Iterator rIt = publicKeyRingCollection.getKeyRings(); @@ -171,7 +173,7 @@ public class DownloadUtil extends Task { // Read signature from file is = PGPUtil.getDecoderStream(new FileInputStream(sigFile)); - PGPObjectFactory pgpObjectFactory = new PGPObjectFactory(is); + PGPObjectFactory pgpObjectFactory = new PGPObjectFactory(is, new JcaKeyFingerprintCalculator()); Object o = pgpObjectFactory.nextObject(); if (o instanceof PGPSignatureList) { PGPSignatureList signatureList = (PGPSignatureList) o; @@ -185,10 +187,10 @@ public class DownloadUtil extends Task { System.out.format("KeyID used in signature: %X\n", pgpSignature.getKeyID()); publicKey = pgpPublicKeyRing.getPublicKey(pgpSignature.getKeyID()); System.out.format("The ID of the selected key is %X\n", publicKey.getKeyID()); - pgpSignature.initVerify(publicKey, "BC"); + pgpSignature.init(new BcPGPContentVerifierBuilderProvider(), publicKey); // Read file to verify - try { + //try { byte[] data = new byte[1024]; is = new DataInputStream(new BufferedInputStream(new FileInputStream(dataFile))); while (true) { @@ -203,12 +205,14 @@ public class DownloadUtil extends Task { result = pgpSignature.verify(); System.out.println("The signature turned out to be " + (result ? "good." : "bad.")); return result; + /* } catch (SignatureException sigExc) { try { is.close(); } catch (IOException ioExc) {} throw sigExc; } + */ } public String getFileName() {return this.fileName;} diff --git a/common/src/main/java/io/bisq/common/util/Utilities.java b/common/src/main/java/io/bisq/common/util/Utilities.java index 19fde29ceb..bb11a35385 100644 --- a/common/src/main/java/io/bisq/common/util/Utilities.java +++ b/common/src/main/java/io/bisq/common/util/Utilities.java @@ -22,6 +22,7 @@ import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.gson.*; import io.bisq.common.crypto.LimitedKeyStrengthException; +import javafx.scene.control.ProgressIndicator; import javafx.scene.input.*; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RandomStringUtils; @@ -202,6 +203,32 @@ public class Utilities { } } + /** + * Creates and starts a Task for background downloading + * @param fileURL URL of file to be downloaded + * @param saveDir Directory to save file to + * @param indicator Progress indicator, can be {@code null} + * @param downloadType enum to identify downloaded files after completion, options are {INST, KEY, SIG, MISC} + * @param index For coordination between key and sig files + * @return The task handling the download + * @throws IOException + */ + public static DownloadUtil downloadFile(String fileURL, String saveDir, @Nullable ProgressIndicator indicator, DownloadType downloadType, byte index) throws IOException { + DownloadUtil task; + if (saveDir != null) + task = new DownloadUtil(fileURL, saveDir, downloadType, index); + else + task = new DownloadUtil(fileURL, downloadType, index); // Tries to use system temp directory + if (indicator != null) { + indicator.progressProperty().unbind(); + indicator.progressProperty().bind(task.progressProperty()); + } + Thread th = new Thread(task); + th.start(); + // TODO: check for problems when creating task + return task; + } + public static void printSystemLoad() { Runtime runtime = Runtime.getRuntime(); long free = runtime.freeMemory() / 1024 / 1024; diff --git a/common/src/main/java/io/bitsquare/common/util/Utilities.java b/common/src/main/java/io/bitsquare/common/util/Utilities.java deleted file mode 100644 index fcc0f8597f..0000000000 --- a/common/src/main/java/io/bitsquare/common/util/Utilities.java +++ /dev/null @@ -1,494 +0,0 @@ -/* - * 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 . - */ - -package io.bitsquare.common.util; - -import com.google.common.base.Charsets; -import com.google.common.io.CharStreams; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; -import com.google.common.util.concurrent.ThreadFactoryBuilder; -import com.google.gson.*; -import io.bitsquare.io.LookAheadObjectInputStream; -import javafx.concurrent.Task; -import javafx.scene.control.ProgressIndicator; -import javafx.scene.input.Clipboard; -import javafx.scene.input.ClipboardContent; -import org.apache.commons.lang3.RandomStringUtils; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nullable; -import javax.crypto.Cipher; -import java.awt.*; -import java.io.*; -import java.net.URI; -import java.net.URLConnection; -import java.security.NoSuchAlgorithmException; -import java.util.Locale; -import java.util.Random; -import java.util.concurrent.*; - - -/** - * General utilities - */ -public class Utilities { - private static final Logger log = LoggerFactory.getLogger(Utilities.class); - private static long lastTimeStamp = System.currentTimeMillis(); - public static final String LB = System.getProperty("line.separator"); - public static final String LB2 = LB + LB; - - public static String objectToJson(Object object) { - Gson gson = new GsonBuilder() - .setExclusionStrategies(new AnnotationExclusionStrategy()) - /*.excludeFieldsWithModifiers(Modifier.TRANSIENT)*/ - /* .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)*/ - .setPrettyPrinting() - .create(); - return gson.toJson(object); - } - - public static ListeningExecutorService getListeningExecutorService(String name, - int corePoolSize, - int maximumPoolSize, - long keepAliveTimeInSec) { - return MoreExecutors.listeningDecorator(getThreadPoolExecutor(name, corePoolSize, maximumPoolSize, keepAliveTimeInSec)); - } - - public static ThreadPoolExecutor getThreadPoolExecutor(String name, - int corePoolSize, - int maximumPoolSize, - long keepAliveTimeInSec) { - final ThreadFactory threadFactory = new ThreadFactoryBuilder() - .setNameFormat(name) - .setDaemon(true) - .build(); - ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTimeInSec, - TimeUnit.SECONDS, new ArrayBlockingQueue<>(maximumPoolSize), threadFactory); - executor.allowCoreThreadTimeOut(true); - executor.setRejectedExecutionHandler((r, e) -> { - log.debug("RejectedExecutionHandler called"); - }); - return executor; - } - - - public static ScheduledThreadPoolExecutor getScheduledThreadPoolExecutor(String name, - int corePoolSize, - int maximumPoolSize, - long keepAliveTimeInSec) { - final ThreadFactory threadFactory = new ThreadFactoryBuilder() - .setNameFormat(name) - .setDaemon(true) - .setPriority(Thread.MIN_PRIORITY) - .build(); - ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(corePoolSize, threadFactory); - executor.setKeepAliveTime(keepAliveTimeInSec, TimeUnit.SECONDS); - executor.allowCoreThreadTimeOut(true); - executor.setMaximumPoolSize(maximumPoolSize); - executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); - executor.setRejectedExecutionHandler((r, e) -> { - log.debug("RejectedExecutionHandler called"); - }); - return executor; - } - - public static boolean isUnix() { - return isOSX() || isLinux() || getOSName().contains("freebsd"); - } - - public static boolean isWindows() { - return getOSName().contains("win"); - } - - public static boolean isOSX() { - return getOSName().contains("mac") || getOSName().contains("darwin"); - } - - public static boolean isLinux() { - return getOSName().contains("linux"); - } - - private static String getOSName() { - return System.getProperty("os.name").toLowerCase(Locale.US); - } - - public static String getOSArchitecture() { - String osArch = System.getProperty("os.arch"); - if (isWindows()) { - // See: Like always windows needs extra treatment - // https://stackoverflow.com/questions/20856694/how-to-find-the-os-bit-type - String arch = System.getenv("PROCESSOR_ARCHITECTURE"); - String wow64Arch = System.getenv("PROCESSOR_ARCHITEW6432"); - return arch.endsWith("64") - || wow64Arch != null && wow64Arch.endsWith("64") - ? "64" : "32"; - } else if (osArch.contains("arm")) { - // armv8 is 64 bit, armv7l is 32 bit - return osArch.contains("64") || osArch.contains("v8") ? "64" : "32"; - } else if (isLinux()) { - return osArch.startsWith("i") ? "32" : "64"; - } else { - return osArch.contains("64") ? "64" : osArch; - } - } - - public static void printSysInfo() { - log.info("os.name: " + System.getProperty("os.name")); - log.info("os.version: " + System.getProperty("os.version")); - log.info("os.arch: " + System.getProperty("os.arch")); - log.info("sun.arch.data.model: " + getJVMArchitecture()); - log.info("JRE: " + System.getProperty("java.runtime.version", "-") + " (" + System.getProperty("java.vendor", "-") + ")"); - log.info("JVM: " + System.getProperty("java.vm.version", "-") + " (" + System.getProperty("java.vm.name", "-") + ")"); - } - - public static String getJVMArchitecture() { - return System.getProperty("sun.arch.data.model"); - } - - public static boolean isCorrectOSArchitecture() { - boolean result = getOSArchitecture().endsWith(getJVMArchitecture()); - if (!result) { - log.warn("System.getProperty(\"os.arch\") " + System.getProperty("os.arch")); - log.warn("System.getenv(\"ProgramFiles(x86)\") " + System.getenv("ProgramFiles(x86)")); - log.warn("System.getenv(\"PROCESSOR_ARCHITECTURE\")" + System.getenv("PROCESSOR_ARCHITECTURE")); - log.warn("System.getenv(\"PROCESSOR_ARCHITEW6432\") " + System.getenv("PROCESSOR_ARCHITEW6432")); - log.warn("System.getProperty(\"sun.arch.data.model\") " + System.getProperty("sun.arch.data.model")); - } - return result; - } - - public static void openURI(URI uri) throws IOException { - if (!isLinux() - && Desktop.isDesktopSupported() - && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { - Desktop.getDesktop().browse(uri); - } else { - // Maybe Application.HostServices works in those cases? - // HostServices hostServices = getHostServices(); - // hostServices.showDocument(uri.toString()); - - // On Linux Desktop is poorly implemented. - // See https://stackoverflow.com/questions/18004150/desktop-api-is-not-supported-on-the-current-platform - if (!DesktopUtil.browse(uri)) - throw new IOException("Failed to open URI: " + uri.toString()); - } - } - - public static void openDirectory(File directory) throws IOException { - if (!isLinux() - && Desktop.isDesktopSupported() - && Desktop.getDesktop().isSupported(Desktop.Action.OPEN)) { - Desktop.getDesktop().open(directory); - } else { - // Maybe Application.HostServices works in those cases? - // HostServices hostServices = getHostServices(); - // hostServices.showDocument(uri.toString()); - - // On Linux Desktop is poorly implemented. - // See https://stackoverflow.com/questions/18004150/desktop-api-is-not-supported-on-the-current-platform - if (!DesktopUtil.open(directory)) - throw new IOException("Failed to open directory: " + directory.toString()); - } - } - - /** - * Creates and starts a Task for background downloading - * @param fileURL URL of file to be downloaded - * @param saveDir Directory to save file to - * @param indicator Progress indicator, can be {@code null} - * @param downloadType enum to identify downloaded files after completion, options are {INST, KEY, SIG, MISC} - * @param index For coordination between key and sig files - * @return The task handling the download - * @throws IOException - */ - public static DownloadUtil downloadFile(String fileURL, String saveDir, @Nullable ProgressIndicator indicator, DownloadType downloadType, byte index) throws IOException { - DownloadUtil task; - if (saveDir != null) - task = new DownloadUtil(fileURL, saveDir, downloadType, index); - else - task = new DownloadUtil(fileURL, downloadType, index); // Tries to use system temp directory - if (indicator != null) { - indicator.progressProperty().unbind(); - indicator.progressProperty().bind(task.progressProperty()); - } - Thread th = new Thread(task); - th.start(); - // TODO: check for problems when creating task - return task; - } - - public static void printSystemLoad() { - Runtime runtime = Runtime.getRuntime(); - long free = runtime.freeMemory() / 1024 / 1024; - long total = runtime.totalMemory() / 1024 / 1024; - long used = total - free; - log.info("System load (no. threads/used memory (MB)): " + Thread.activeCount() + "/" + used); - } - - public static void copyToClipboard(String content) { - try { - if (content != null && content.length() > 0) { - Clipboard clipboard = Clipboard.getSystemClipboard(); - ClipboardContent clipboardContent = new ClipboardContent(); - clipboardContent.putString(content); - clipboard.setContent(clipboardContent); - } - } catch (Throwable e) { - log.error("copyToClipboard failed " + e.getMessage()); - e.printStackTrace(); - } - } - - public static byte[] concatByteArrays(byte[]... arrays) { - int totalLength = 0; - for (byte[] array : arrays) { - totalLength += array.length; - } - - byte[] result = new byte[totalLength]; - int currentIndex = 0; - for (byte[] array : arrays) { - System.arraycopy(array, 0, result, currentIndex, array.length); - currentIndex += array.length; - } - return result; - } - - public static T jsonToObject(String jsonString, Class classOfT) { - Gson gson = - new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).setPrettyPrinting().create(); - return gson.fromJson(jsonString, classOfT); - } - - -/* public static Object deserializeHexStringToObject(String serializedHexString) { - Object result = null; - try { - ByteArrayInputStream byteInputStream = - new ByteArrayInputStream(org.bitcoinj.core.Utils.parseAsHexOrBase58(serializedHexString)); - - try (ObjectInputStream objectInputStream = new LookAheadObjectInputStream(byteInputStream)) { - result = objectInputStream.readObject(); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } finally { - byteInputStream.close(); - - } - - } catch (IOException i) { - i.printStackTrace(); - } - return result; - } - - - public static String serializeObjectToHexString(Serializable serializable) { - String result = null; - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - try { - ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); - objectOutputStream.writeObject(serializable); - - result = org.bitcoinj.core.Utils.HEX.encode(byteArrayOutputStream.toByteArray()); - byteArrayOutputStream.close(); - objectOutputStream.close(); - - } catch (IOException e) { - e.printStackTrace(); - } - return result; - }*/ - - public static T deserialize(byte[] data) { - ByteArrayInputStream bis = new ByteArrayInputStream(data); - ObjectInput in = null; - Object result = null; - try { - in = new LookAheadObjectInputStream(bis, true); - result = in.readObject(); - if (!(result instanceof Serializable)) - throw new RuntimeException("Object not of type Serializable"); - } catch (Exception e) { - e.printStackTrace(); - } finally { - try { - bis.close(); - } catch (IOException ex) { - // ignore close exception - } - try { - if (in != null) { - in.close(); - } - } catch (IOException ex) { - // ignore close exception - } - } - return (T) result; - } - - public static byte[] serialize(Serializable object) { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ObjectOutput out = null; - byte[] result = null; - try { - out = new ObjectOutputStream(bos); - out.writeObject(object); - out.flush(); - result = bos.toByteArray().clone(); - } catch (IOException e) { - e.printStackTrace(); - } finally { - try { - if (out != null) { - out.close(); - } - } catch (IOException ignore) { - } - try { - bos.close(); - } catch (IOException ignore) { - } - } - return result; - } - - private static void printElapsedTime(String msg) { - if (!msg.isEmpty()) { - msg += " / "; - } - long timeStamp = System.currentTimeMillis(); - log.debug(msg + "Elapsed: " + String.valueOf(timeStamp - lastTimeStamp)); - lastTimeStamp = timeStamp; - } - - public static void printElapsedTime() { - printElapsedTime(""); - } - - - public static Object copy(Serializable orig) throws IOException, ClassNotFoundException { - try { - // Write the object out to a byte array - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ObjectOutputStream out = new ObjectOutputStream(bos); - out.writeObject(orig); - out.flush(); - out.close(); - - // Make an input stream from the byte array and read - // a copy of the object back in. - ObjectInputStream in = new LookAheadObjectInputStream(new ByteArrayInputStream(bos.toByteArray()), true); - Object obj = in.readObject(); - return obj; - } catch (IOException | ClassNotFoundException e) { - e.printStackTrace(); - throw e; - } - } - - public static String readTextFileFromServer(String url, String userAgent) throws IOException { - URLConnection connection = URI.create(url).toURL().openConnection(); - connection.setDoOutput(true); - connection.setUseCaches(false); - connection.setConnectTimeout((int) TimeUnit.SECONDS.toMillis(10)); - connection.addRequestProperty("User-Agent", userAgent); - connection.connect(); - try (InputStream inputStream = connection.getInputStream()) { - return CharStreams.toString(new InputStreamReader(inputStream, Charsets.UTF_8)); - } catch (IOException e) { - e.printStackTrace(); - throw e; - } - } - - public static void setThreadName(String name) { - Thread.currentThread().setName(name + "-" + new Random().nextInt(10000)); - } - - private static class AnnotationExclusionStrategy implements ExclusionStrategy { - @Override - public boolean shouldSkipField(FieldAttributes f) { - return f.getAnnotation(JsonExclude.class) != null; - } - - @Override - public boolean shouldSkipClass(Class clazz) { - return false; - } - } - - public static void checkCryptoPolicySetup() throws NoSuchAlgorithmException, LimitedKeyStrengthException { - if (Cipher.getMaxAllowedKeyLength("AES") > 128) - log.debug("Congratulations, you have unlimited key length support!"); - else - throw new LimitedKeyStrengthException(); - } - - public static String toTruncatedString(Object message, int maxLenght) { - return StringUtils.abbreviate(message.toString(), maxLenght).replace("\n", ""); - } - - public static String toTruncatedString(Object message) { - return toTruncatedString(message, 200); - } - - public static String getRandomPrefix(int minLength, int maxLength) { - int length = minLength + new Random().nextInt(maxLength - minLength + 1); - String result; - switch (new Random().nextInt(3)) { - case 0: - result = RandomStringUtils.randomAlphabetic(length); - break; - case 1: - result = RandomStringUtils.randomNumeric(length); - break; - case 2: - default: - result = RandomStringUtils.randomAlphanumeric(length); - } - - switch (new Random().nextInt(3)) { - case 0: - result = result.toUpperCase(); - break; - case 1: - result = result.toLowerCase(); - break; - case 2: - default: - } - - return result; - } - - public static String getShortId(String id) { - return getShortId(id, "-"); - } - - public static String getShortId(String id, String sep) { - String[] chunks = id.split(sep); - if (chunks.length > 0) - return chunks[0]; - else - return id.substring(0, Math.min(8, id.length())); - } -} diff --git a/core/src/main/java/io/bitsquare/alert/AlertManager.java b/core/src/main/java/io/bitsquare/alert/AlertManager.java deleted file mode 100644 index deaa35db71..0000000000 --- a/core/src/main/java/io/bitsquare/alert/AlertManager.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * 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 . - */ - -package io.bitsquare.alert; - -import com.google.inject.Inject; -import com.google.inject.name.Named; -import io.bitsquare.app.AppOptionKeys; -import io.bitsquare.common.crypto.KeyRing; -import io.bitsquare.p2p.P2PService; -import io.bitsquare.p2p.storage.HashMapChangedListener; -import io.bitsquare.p2p.storage.storageentry.ProtectedStorageEntry; -import io.bitsquare.user.User; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.ReadOnlyObjectProperty; -import javafx.beans.property.SimpleObjectProperty; -import org.bitcoinj.core.ECKey; -import org.bitcoinj.core.Utils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.math.BigInteger; -import java.security.SignatureException; - -import static org.bitcoinj.core.Utils.HEX; - -public class AlertManager { - private static final Logger log = LoggerFactory.getLogger(AlertManager.class); - - private P2PService p2PService; - private final KeyRing keyRing; - private final User user; - private final ObjectProperty alertMessageProperty = new SimpleObjectProperty<>(); - - // Pub key for developer global alert message - private static final String pubKeyAsHex = "027a381b5333a56e1cc3d90d3a7d07f26509adf7029ed06fc997c656621f8da1ee"; - private ECKey alertSigningKey; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor, Initialization - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public AlertManager(P2PService p2PService, KeyRing keyRing, User user, @Named(AppOptionKeys.IGNORE_DEV_MSG_KEY) boolean ignoreDevMsg) { - this.p2PService = p2PService; - this.keyRing = keyRing; - this.user = user; - - if (!ignoreDevMsg) { - p2PService.addHashSetChangedListener(new HashMapChangedListener() { - @Override - public void onAdded(ProtectedStorageEntry data) { - if (data.getStoragePayload() instanceof Alert) { - Alert alert = (Alert) data.getStoragePayload(); - if (verifySignature(alert)) - alertMessageProperty.set(alert); - } - } - - @Override - public void onRemoved(ProtectedStorageEntry data) { - if (data.getStoragePayload() instanceof Alert) { - Alert alert = (Alert) data.getStoragePayload(); - if (verifySignature(alert)) - alertMessageProperty.set(null); - } - } - }); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public ReadOnlyObjectProperty alertMessageProperty() { - return alertMessageProperty; - } - - public boolean addAlertMessageIfKeyIsValid(Alert alert, String privKeyString) { - // if there is a previous message we remove that first - if (user.getDevelopersAlert() != null) - removeAlertMessageIfKeyIsValid(privKeyString); - - boolean isKeyValid = isKeyValid(privKeyString); - if (isKeyValid) { - signAndAddSignatureToAlertMessage(alert); - user.setDevelopersAlert(alert); - boolean result = p2PService.addData(alert, true); - if (result) { - log.trace("Add alertMessage to network was successful. AlertMessage = " + alert); - } - - } - return isKeyValid; - } - - public boolean removeAlertMessageIfKeyIsValid(String privKeyString) { - Alert alert = user.getDevelopersAlert(); - if (isKeyValid(privKeyString) && alert != null) { - if (p2PService.removeData(alert, true)) - log.trace("Remove alertMessage from network was successful. AlertMessage = " + alert); - - user.setDevelopersAlert(null); - return true; - } else { - return false; - } - } - - private boolean isKeyValid(String privKeyString) { - try { - alertSigningKey = ECKey.fromPrivate(new BigInteger(1, HEX.decode(privKeyString))); - return pubKeyAsHex.equals(Utils.HEX.encode(alertSigningKey.getPubKey())); - } catch (Throwable t) { - return false; - } - } - - private void signAndAddSignatureToAlertMessage(Alert alert) { - String alertMessageAsHex = Utils.HEX.encode(alert.message.getBytes()); - String signatureAsBase64 = alertSigningKey.signMessage(alertMessageAsHex); - alert.setSigAndPubKey(signatureAsBase64, keyRing.getSignatureKeyPair().getPublic()); - } - - private boolean verifySignature(Alert alert) { - String alertMessageAsHex = Utils.HEX.encode(alert.message.getBytes()); - try { - ECKey.fromPublicOnly(HEX.decode(pubKeyAsHex)).verifyMessage(alertMessageAsHex, alert.getSignatureAsBase64()); - return true; - } catch (SignatureException e) { - log.warn("verifySignature failed"); - return false; - } - } -} diff --git a/core/src/main/java/io/bitsquare/arbitration/ArbitratorManager.java b/core/src/main/java/io/bitsquare/arbitration/ArbitratorManager.java deleted file mode 100644 index fe521e9719..0000000000 --- a/core/src/main/java/io/bitsquare/arbitration/ArbitratorManager.java +++ /dev/null @@ -1,299 +0,0 @@ -/* - * 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 . - */ - -package io.bitsquare.arbitration; - -import com.google.inject.Inject; -import io.bitsquare.common.Timer; -import io.bitsquare.common.UserThread; -import io.bitsquare.common.crypto.KeyRing; -import io.bitsquare.common.handlers.ErrorMessageHandler; -import io.bitsquare.common.handlers.ResultHandler; -import io.bitsquare.p2p.BootstrapListener; -import io.bitsquare.p2p.NodeAddress; -import io.bitsquare.p2p.P2PService; -import io.bitsquare.p2p.storage.HashMapChangedListener; -import io.bitsquare.p2p.storage.storageentry.ProtectedStorageEntry; -import io.bitsquare.user.Preferences; -import io.bitsquare.user.User; -import javafx.collections.FXCollections; -import javafx.collections.ObservableMap; -import org.bitcoinj.core.ECKey; -import org.bitcoinj.core.Utils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nullable; -import java.math.BigInteger; -import java.security.PublicKey; -import java.security.SignatureException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; -import java.util.stream.Collectors; - -import static org.bitcoinj.core.Utils.HEX; - -public class ArbitratorManager { - private static final Logger log = LoggerFactory.getLogger(ArbitratorManager.class); - - /////////////////////////////////////////////////////////////////////////////////////////// - // Static - /////////////////////////////////////////////////////////////////////////////////////////// - - private static final long REPUBLISH_MILLIS = Arbitrator.TTL / 2; - private static final long RETRY_REPUBLISH_SEC = 5; - private static final long REPEATED_REPUBLISH_AT_STARTUP_SEC = 60; - - // Keys for invited arbitrators in bootstrapping phase (before registration is open to anyone and security payment is implemented) - // For developers we add here 2 test keys so one can setup an arbitrator by adding that test pubKey - // to the publicKeys list and use the test PrivKey for arbitrator registration. - // PrivKey for dev testing: 6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a - // Matching pubKey for dev testing: 027a381b5333a56e1cc3d90d3a7d07f26509adf7029ed06fc997c656621f8da1ee - private static final List publicKeys = new ArrayList<>(Arrays.asList( - "03697a499d24f497b3c46bf716318231e46c4e6a685a4e122d8e2a2b229fa1f4b8", - "0365c6af94681dbee69de1851f98d4684063bf5c2d64b1c73ed5d90434f375a054", - "031c502a60f9dbdb5ae5e438a79819e4e1f417211dd537ac12c9bc23246534c4bd", - "02c1e5a242387b6d5319ce27246cea6edaaf51c3550591b528d2578a4753c56c2c", - "025c319faf7067d9299590dd6c97fe7e56cd4dac61205ccee1cd1fc390142390a2", - "038f6e24c2bfe5d51d0a290f20a9a657c270b94ef2b9c12cd15ca3725fa798fc55", - "0255256ff7fb615278c4544a9bbd3f5298b903b8a011cd7889be19b6b1c45cbefe", - "024a3a37289f08c910fbd925ebc72b946f33feaeff451a4738ee82037b4cda2e95", - "02a88b75e9f0f8afba1467ab26799dcc38fd7a6468fb2795444b425eb43e2c10bd", - "02349a51512c1c04c67118386f4d27d768c5195a83247c150a4b722d161722ba81", - "03f718a2e0dc672c7cdec0113e72c3322efc70412bb95870750d25c32cd98de17d", - "028ff47ee2c56e66313928975c58fa4f1b19a0f81f3a96c4e9c9c3c6768075509e", - "02b517c0cbc3a49548f448ddf004ed695c5a1c52ec110be1bfd65fa0ca0761c94b", - "03df837a3a0f3d858e82f3356b71d1285327f101f7c10b404abed2abc1c94e7169", - "0203a90fb2ab698e524a5286f317a183a84327b8f8c3f7fa4a98fec9e1cefd6b72", - "023c99cc073b851c892d8c43329ca3beb5d2213ee87111af49884e3ce66cbd5ba5", - "027a381b5333a56e1cc3d90d3a7d07f26509adf7029ed06fc997c656621f8da1ee" - )); - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Instance fields - /////////////////////////////////////////////////////////////////////////////////////////// - - private final KeyRing keyRing; - private final ArbitratorService arbitratorService; - private final User user; - private Preferences preferences; - private final ObservableMap arbitratorsObservableMap = FXCollections.observableHashMap(); - private final List persistedAcceptedArbitrators; - private Timer republishArbitratorTimer, retryRepublishArbitratorTimer; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public ArbitratorManager(KeyRing keyRing, ArbitratorService arbitratorService, User user, Preferences preferences) { - this.keyRing = keyRing; - this.arbitratorService = arbitratorService; - this.user = user; - this.preferences = preferences; - - persistedAcceptedArbitrators = new ArrayList<>(user.getAcceptedArbitrators()); - user.clearAcceptedArbitrators(); - - arbitratorService.addHashSetChangedListener(new HashMapChangedListener() { - @Override - public void onAdded(ProtectedStorageEntry data) { - if (data.getStoragePayload() instanceof Arbitrator) - updateArbitratorMap(); - } - - @Override - public void onRemoved(ProtectedStorageEntry data) { - if (data.getStoragePayload() instanceof Arbitrator) - updateArbitratorMap(); - } - }); - } - - public void shutDown() { - stopRepublishArbitratorTimer(); - stopRetryRepublishArbitratorTimer(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public void onAllServicesInitialized() { - if (user.getRegisteredArbitrator() != null) { - P2PService p2PService = arbitratorService.getP2PService(); - if (p2PService.isBootstrapped()) - isBootstrapped(); - else - p2PService.addP2PServiceListener(new BootstrapListener() { - @Override - public void onBootstrapComplete() { - isBootstrapped(); - } - }); - } - - updateArbitratorMap(); - } - - private void isBootstrapped() { - if (republishArbitratorTimer == null) { - republishArbitratorTimer = UserThread.runPeriodically(this::republishArbitrator, REPUBLISH_MILLIS, TimeUnit.MILLISECONDS); - UserThread.runAfter(this::republishArbitrator, REPEATED_REPUBLISH_AT_STARTUP_SEC); - republishArbitrator(); - } - } - - public void updateArbitratorMap() { - Map map = arbitratorService.getArbitrators(); - arbitratorsObservableMap.clear(); - Map filtered = map.values().stream() - .filter(e -> isPublicKeyInList(Utils.HEX.encode(e.getRegistrationPubKey())) - && verifySignature(e.getPubKeyRing().getSignaturePubKey(), e.getRegistrationPubKey(), e.getRegistrationSignature())) - .collect(Collectors.toMap(Arbitrator::getArbitratorNodeAddress, Function.identity())); - - arbitratorsObservableMap.putAll(filtered); - arbitratorsObservableMap.values().stream() - .filter(arbitrator -> persistedAcceptedArbitrators.contains(arbitrator)) - .forEach(user::addAcceptedArbitrator); - - if (preferences.getAutoSelectArbitrators()) { - arbitratorsObservableMap.values().stream() - .filter(user::hasMatchingLanguage) - .forEach(user::addAcceptedArbitrator); - } else { - // if we don't have any arbitrator we set all matching - // we use a delay as we might get our matching arbitrator a bit delayed (first we get one we did not selected - // then we get our selected one - we don't want to activate the first in that case) - UserThread.runAfter(() -> { - if (user.getAcceptedArbitrators().isEmpty()) { - arbitratorsObservableMap.values().stream() - .filter(user::hasMatchingLanguage) - .forEach(user::addAcceptedArbitrator); - } - }, 100, TimeUnit.MILLISECONDS); - } - } - - public void addArbitrator(Arbitrator arbitrator, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { - user.setRegisteredArbitrator(arbitrator); - arbitratorsObservableMap.put(arbitrator.getArbitratorNodeAddress(), arbitrator); - arbitratorService.addArbitrator(arbitrator, - () -> { - log.debug("Arbitrator successfully saved in P2P network"); - resultHandler.handleResult(); - - if (arbitratorsObservableMap.size() > 0) - UserThread.runAfter(this::updateArbitratorMap, 100, TimeUnit.MILLISECONDS); - }, - errorMessageHandler::handleErrorMessage); - } - - public void removeArbitrator(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { - Arbitrator registeredArbitrator = user.getRegisteredArbitrator(); - if (registeredArbitrator != null) { - user.setRegisteredArbitrator(null); - arbitratorsObservableMap.remove(registeredArbitrator.getArbitratorNodeAddress()); - arbitratorService.removeArbitrator(registeredArbitrator, - () -> { - log.debug("Arbitrator successfully removed from P2P network"); - resultHandler.handleResult(); - }, - errorMessageHandler::handleErrorMessage); - } - } - - public ObservableMap getArbitratorsObservableMap() { - return arbitratorsObservableMap; - } - - // A private key is handed over to selected arbitrators for registration. - // An invited arbitrator will sign at registration his storageSignaturePubKey with that private key and attach the signature and pubKey to his data. - // Other users will check the signature with the list of public keys hardcoded in the app. - public String signStorageSignaturePubKey(ECKey key) { - String keyToSignAsHex = Utils.HEX.encode(keyRing.getPubKeyRing().getSignaturePubKey().getEncoded()); - return key.signMessage(keyToSignAsHex); - } - - @Nullable - public ECKey getRegistrationKey(String privKeyBigIntString) { - try { - return ECKey.fromPrivate(new BigInteger(1, HEX.decode(privKeyBigIntString))); - } catch (Throwable t) { - return null; - } - } - - public boolean isPublicKeyInList(String pubKeyAsHex) { - return publicKeys.contains(pubKeyAsHex); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void republishArbitrator() { - Arbitrator registeredArbitrator = user.getRegisteredArbitrator(); - if (registeredArbitrator != null) { - addArbitrator(registeredArbitrator, - this::updateArbitratorMap, - errorMessage -> { - if (retryRepublishArbitratorTimer == null) - retryRepublishArbitratorTimer = UserThread.runPeriodically(() -> { - stopRetryRepublishArbitratorTimer(); - republishArbitrator(); - }, RETRY_REPUBLISH_SEC); - } - ); - } - } - - private boolean verifySignature(PublicKey storageSignaturePubKey, byte[] registrationPubKey, String signature) { - String keyToSignAsHex = Utils.HEX.encode(storageSignaturePubKey.getEncoded()); - try { - ECKey key = ECKey.fromPublicOnly(registrationPubKey); - key.verifyMessage(keyToSignAsHex, signature); - return true; - } catch (SignatureException e) { - log.warn("verifySignature failed"); - return false; - } - } - - - private void stopRetryRepublishArbitratorTimer() { - if (retryRepublishArbitratorTimer != null) { - retryRepublishArbitratorTimer.stop(); - retryRepublishArbitratorTimer = null; - } - } - - private void stopRepublishArbitratorTimer() { - if (republishArbitratorTimer != null) { - republishArbitratorTimer.stop(); - republishArbitratorTimer = null; - } - } -} diff --git a/gui/src/main/java/io/bisq/gui/main/MainViewModel.java b/gui/src/main/java/io/bisq/gui/main/MainViewModel.java index f2ccebfd3a..5d00f8122b 100644 --- a/gui/src/main/java/io/bisq/gui/main/MainViewModel.java +++ b/gui/src/main/java/io/bisq/gui/main/MainViewModel.java @@ -83,6 +83,7 @@ import io.bisq.network.p2p.network.CloseConnectionReason; import io.bisq.network.p2p.network.Connection; import io.bisq.network.p2p.network.ConnectionListener; import io.bisq.network.p2p.peers.keepalive.messages.Ping; +import io.bisq.gui.main.overlays.windows.DisplayUpdateDownloadWindow; import javafx.beans.property.*; import javafx.beans.value.ChangeListener; import javafx.collections.FXCollections; @@ -1019,10 +1020,11 @@ public class MainViewModel implements ViewModel { private void displayAlertIfPresent(Alert alert) { boolean alreadyDisplayed = alert != null && alert.equals(user.getDisplayedAlert()); user.setDisplayedAlert(alert); - if (alert != null && - !alreadyDisplayed && - (!alert.isUpdateInfo() || alert.isNewVersion())) - new DisplayAlertMessageWindow().alertMessage(alert).show(); + if (alert != null && !alreadyDisplayed) + if (alert.isUpdateInfo() || alert.isNewVersion()) + new DisplayUpdateDownloadWindow().alertMessage(alert).show(); + else + new DisplayAlertMessageWindow().alertMessage(alert).show(); } private void displayPrivateNotification(PrivateNotificationPayload privateNotification) { diff --git a/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/DisplayUpdateDownloadWindow.java b/gui/src/main/java/io/bisq/gui/main/overlays/windows/DisplayUpdateDownloadWindow.java similarity index 94% rename from gui/src/main/java/io/bitsquare/gui/main/overlays/windows/DisplayUpdateDownloadWindow.java rename to gui/src/main/java/io/bisq/gui/main/overlays/windows/DisplayUpdateDownloadWindow.java index 542ec3188f..36db932972 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/DisplayUpdateDownloadWindow.java +++ b/gui/src/main/java/io/bisq/gui/main/overlays/windows/DisplayUpdateDownloadWindow.java @@ -15,13 +15,13 @@ * along with Bitsquare. If not, see . */ -package io.bitsquare.gui.main.overlays.windows; +package io.bisq.gui.main.overlays.windows; -import io.bitsquare.alert.Alert; -import io.bitsquare.common.util.DownloadType; -import io.bitsquare.common.util.DownloadUtil; -import io.bitsquare.common.util.Utilities; -import io.bitsquare.gui.main.overlays.Overlay; +import io.bisq.core.alert.Alert; +import io.bisq.gui.main.overlays.Overlay; +import io.bisq.common.util.DownloadType; +import io.bisq.common.util.DownloadUtil; +import io.bisq.common.util.Utilities; import javafx.concurrent.Task; import javafx.concurrent.WorkerStateEvent; import javafx.event.EventHandler; @@ -38,9 +38,8 @@ import java.io.File; import java.io.IOException; import static com.google.common.base.Preconditions.checkNotNull; -import static io.bitsquare.gui.util.FormBuilder.addButton; -import static io.bitsquare.gui.util.FormBuilder.addLabelHyperlinkWithIcon; -import static io.bitsquare.gui.util.FormBuilder.addMultilineLabel; +import static io.bisq.gui.util.FormBuilder.addButton; +import static io.bisq.gui.util.FormBuilder.addMultilineLabel; // TODO: For future use sigFile and key download calls have to loop through keyIDs and verifySignature needs to be called for all sigs/keys @@ -87,7 +86,7 @@ public class DisplayUpdateDownloadWindow extends Overlay. - */ - -package io.bitsquare.gui.main; - -import com.google.inject.Inject; -import io.bitsquare.alert.Alert; -import io.bitsquare.alert.AlertManager; -import io.bitsquare.alert.PrivateNotification; -import io.bitsquare.alert.PrivateNotificationManager; -import io.bitsquare.app.BitsquareApp; -import io.bitsquare.app.DevFlags; -import io.bitsquare.app.Log; -import io.bitsquare.app.Version; -import io.bitsquare.arbitration.ArbitratorManager; -import io.bitsquare.arbitration.Dispute; -import io.bitsquare.arbitration.DisputeManager; -import io.bitsquare.btc.AddressEntry; -import io.bitsquare.btc.TradeWalletService; -import io.bitsquare.btc.WalletService; -import io.bitsquare.btc.listeners.BalanceListener; -import io.bitsquare.btc.pricefeed.MarketPrice; -import io.bitsquare.btc.pricefeed.PriceFeedService; -import io.bitsquare.common.Clock; -import io.bitsquare.common.Timer; -import io.bitsquare.common.UserThread; -import io.bitsquare.common.crypto.*; -import io.bitsquare.filter.FilterManager; -import io.bitsquare.gui.Navigation; -import io.bitsquare.gui.common.model.ViewModel; -import io.bitsquare.gui.components.BalanceTextField; -import io.bitsquare.gui.components.BalanceWithConfirmationTextField; -import io.bitsquare.gui.components.TxIdTextField; -import io.bitsquare.gui.main.overlays.notifications.NotificationCenter; -import io.bitsquare.gui.main.overlays.popups.Popup; -import io.bitsquare.gui.main.overlays.windows.*; -import io.bitsquare.gui.util.BSFormatter; -import io.bitsquare.gui.util.GUIUtil; -import io.bitsquare.locale.CurrencyUtil; -import io.bitsquare.locale.TradeCurrency; -import io.bitsquare.p2p.P2PService; -import io.bitsquare.p2p.P2PServiceListener; -import io.bitsquare.p2p.network.CloseConnectionReason; -import io.bitsquare.p2p.network.Connection; -import io.bitsquare.p2p.network.ConnectionListener; -import io.bitsquare.p2p.peers.keepalive.messages.Ping; -import io.bitsquare.payment.CryptoCurrencyAccount; -import io.bitsquare.payment.OKPayAccount; -import io.bitsquare.payment.PaymentAccount; -import io.bitsquare.trade.Trade; -import io.bitsquare.trade.TradeManager; -import io.bitsquare.trade.offer.OpenOffer; -import io.bitsquare.trade.offer.OpenOfferManager; -import io.bitsquare.user.Preferences; -import io.bitsquare.user.User; -import javafx.beans.property.*; -import javafx.beans.value.ChangeListener; -import javafx.collections.FXCollections; -import javafx.collections.ListChangeListener; -import javafx.collections.ObservableList; -import javafx.collections.SetChangeListener; -import org.bitcoinj.core.Address; -import org.bitcoinj.core.Coin; -import org.bitcoinj.core.Transaction; -import org.bitcoinj.core.Wallet; -import org.bitcoinj.store.BlockStoreException; -import org.fxmisc.easybind.EasyBind; -import org.fxmisc.easybind.Subscription; -import org.fxmisc.easybind.monadic.MonadicBinding; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nullable; -import java.security.Security; -import java.util.*; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.stream.Collectors; - -public class MainViewModel implements ViewModel { - private static final Logger log = LoggerFactory.getLogger(MainViewModel.class); - - private final WalletService walletService; - private final TradeWalletService tradeWalletService; - private final ArbitratorManager arbitratorManager; - private final P2PService p2PService; - private final TradeManager tradeManager; - private final OpenOfferManager openOfferManager; - private final DisputeManager disputeManager; - final Preferences preferences; - private final AlertManager alertManager; - private PrivateNotificationManager privateNotificationManager; - private FilterManager filterManager; - private final WalletPasswordWindow walletPasswordWindow; - private AddBitcoinNodesWindow addBitcoinNodesWindow; - private final NotificationCenter notificationCenter; - private final TacWindow tacWindow; - private Clock clock; - private KeyRing keyRing; - private final Navigation navigation; - private final BSFormatter formatter; - - // BTC network - final StringProperty btcInfo = new SimpleStringProperty("Initializing"); - final DoubleProperty btcSyncProgress = new SimpleDoubleProperty(DevFlags.STRESS_TEST_MODE ? 0 : -1); - final StringProperty walletServiceErrorMsg = new SimpleStringProperty(); - final StringProperty btcSplashSyncIconId = new SimpleStringProperty(); - final StringProperty marketPriceCurrencyCode = new SimpleStringProperty(""); - final ObjectProperty typeProperty = new SimpleObjectProperty<>(PriceFeedService.Type.LAST); - final ObjectProperty selectedPriceFeedComboBoxItemProperty = new SimpleObjectProperty<>(); - final BooleanProperty isFiatCurrencyPriceFeedSelected = new SimpleBooleanProperty(true); - final BooleanProperty isCryptoCurrencyPriceFeedSelected = new SimpleBooleanProperty(false); - final StringProperty availableBalance = new SimpleStringProperty(); - final StringProperty reservedBalance = new SimpleStringProperty(); - final StringProperty lockedBalance = new SimpleStringProperty(); - private MonadicBinding btcInfoBinding; - - final StringProperty marketPrice = new SimpleStringProperty("N/A"); - - // P2P network - final StringProperty p2PNetworkInfo = new SimpleStringProperty(); - private MonadicBinding p2PNetworkInfoBinding; - final BooleanProperty splashP2PNetworkAnimationVisible = new SimpleBooleanProperty(true); - final StringProperty p2pNetworkWarnMsg = new SimpleStringProperty(); - final StringProperty p2PNetworkIconId = new SimpleStringProperty(); - final BooleanProperty bootstrapComplete = new SimpleBooleanProperty(); - - // software update - final String version = "v" + Version.VERSION; - - final BooleanProperty showAppScreen = new SimpleBooleanProperty(); - final StringProperty numPendingTradesAsString = new SimpleStringProperty(); - final BooleanProperty showPendingTradesNotification = new SimpleBooleanProperty(); - final StringProperty numOpenDisputesAsString = new SimpleStringProperty(); - final BooleanProperty showOpenDisputesNotification = new SimpleBooleanProperty(); - private final BooleanProperty isSplashScreenRemoved = new SimpleBooleanProperty(); - private final String btcNetworkAsString; - final StringProperty p2pNetworkLabelId = new SimpleStringProperty("footer-pane"); - - private MonadicBinding allServicesDone, tradesAndUIReady; - final PriceFeedService priceFeedService; - private final User user; - private int numBtcPeers = 0; - private Timer checkNumberOfBtcPeersTimer; - private Timer checkNumberOfP2pNetworkPeersTimer; - private final Map disputeIsClosedSubscriptionsMap = new HashMap<>(); - final ObservableList priceFeedComboBoxItems = FXCollections.observableArrayList(); - private MonadicBinding marketPriceBinding; - private Subscription priceFeedAllLoadedSubscription; - private Popup startupTimeoutPopup; - private BooleanProperty p2pNetWorkReady; - private final BooleanProperty walletInitialized = new SimpleBooleanProperty(); - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public MainViewModel(WalletService walletService, TradeWalletService tradeWalletService, - PriceFeedService priceFeedService, - ArbitratorManager arbitratorManager, P2PService p2PService, TradeManager tradeManager, - OpenOfferManager openOfferManager, DisputeManager disputeManager, Preferences preferences, - User user, AlertManager alertManager, PrivateNotificationManager privateNotificationManager, - FilterManager filterManager, WalletPasswordWindow walletPasswordWindow, AddBitcoinNodesWindow addBitcoinNodesWindow, - NotificationCenter notificationCenter, TacWindow tacWindow, Clock clock, - KeyRing keyRing, Navigation navigation, BSFormatter formatter) { - this.priceFeedService = priceFeedService; - this.user = user; - this.walletService = walletService; - this.tradeWalletService = tradeWalletService; - this.arbitratorManager = arbitratorManager; - this.p2PService = p2PService; - this.tradeManager = tradeManager; - this.openOfferManager = openOfferManager; - this.disputeManager = disputeManager; - this.preferences = preferences; - this.alertManager = alertManager; - this.privateNotificationManager = privateNotificationManager; - this.filterManager = filterManager; // Reference so it's initialized and eventlistener gets registered - this.walletPasswordWindow = walletPasswordWindow; - this.addBitcoinNodesWindow = addBitcoinNodesWindow; - this.notificationCenter = notificationCenter; - this.tacWindow = tacWindow; - this.clock = clock; - this.keyRing = keyRing; - this.navigation = navigation; - this.formatter = formatter; - - btcNetworkAsString = formatter.formatBitcoinNetwork(preferences.getBitcoinNetwork()) + - (preferences.getUseTorForBitcoinJ() ? " (using Tor)" : ""); - - TxIdTextField.setPreferences(preferences); - TxIdTextField.setWalletService(walletService); - BalanceTextField.setWalletService(walletService); - BalanceWithConfirmationTextField.setWalletService(walletService); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public void start() { - // TODO need more though how to improve privacy without annoying first time users. - /* String key = "showAddBitcoinNodesWindowKey"; - if (preferences.showAgain(key)) - addBitcoinNodesWindow.dontShowAgainId(key, preferences) - .onClose(() -> { - preferences.dontShowAgain(key, true); - initializeAllServices(); - }) - .onAction(() -> { - preferences.dontShowAgain(key, true); - initializeAllServices(); - }) - .show(); - else - initializeAllServices(); - } - - private void initializeAllServices() {*/ - - Log.traceCall(); - - UserThread.runAfter(tacWindow::showIfNeeded, 2); - - - ChangeListener walletInitializedListener = (observable, oldValue, newValue) -> { - if (newValue && !p2pNetWorkReady.get()) - showStartupTimeoutPopup(); - }; - - Timer startupTimeout = UserThread.runAfter(() -> { - log.warn("startupTimeout called"); - Wallet wallet = walletService.getWallet(); - if (wallet != null && wallet.isEncrypted()) - walletInitialized.addListener(walletInitializedListener); - else - showStartupTimeoutPopup(); - }, 4, TimeUnit.MINUTES); - - p2pNetWorkReady = initP2PNetwork(); - initBitcoinWallet(); - - // need to store it to not get garbage collected - allServicesDone = EasyBind.combine(walletInitialized, p2pNetWorkReady, (a, b) -> a && b); - allServicesDone.subscribe((observable, oldValue, newValue) -> { - if (newValue) { - startupTimeout.stop(); - walletInitialized.removeListener(walletInitializedListener); - onAllServicesInitialized(); - if (startupTimeoutPopup != null) - startupTimeoutPopup.hide(); - } - }); - } - - private void showStartupTimeoutPopup() { - MainView.blur(); - String details; - if (!walletInitialized.get()) { - details = "You still did not get connected to the bitcoin network.\n" + - "If you use Tor for Bitcoin it might be that you got an unstable Tor path.\n" + - "You can wait longer or try to restart."; - } else if (!p2pNetWorkReady.get()) { - details = "You still did not get connected to the P2P network.\n" + - "That can happen sometimes when you got an unstable Tor path.\n" + - "You can wait longer or try to restart."; - } else { - log.error("Startup timeout with unknown problem."); - details = "There is an unknown problem at startup.\n" + - "Please restart and if the problem continues file a bug report."; - } - startupTimeoutPopup = new Popup(); - startupTimeoutPopup.warning("The application could not startup after 4 minutes.\n\n" + - details) - .actionButtonText("Shut down") - .onAction(BitsquareApp.shutDownHandler::run) - .show(); - } - - public void shutDown() { - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Initialisation - /////////////////////////////////////////////////////////////////////////////////////////// - - private BooleanProperty initP2PNetwork() { - Log.traceCall(); - StringProperty bootstrapState = new SimpleStringProperty(); - StringProperty bootstrapWarning = new SimpleStringProperty(); - BooleanProperty hiddenServicePublished = new SimpleBooleanProperty(); - BooleanProperty initialP2PNetworkDataReceived = new SimpleBooleanProperty(); - - p2PNetworkInfoBinding = EasyBind.combine(bootstrapState, bootstrapWarning, p2PService.getNumConnectedPeers(), hiddenServicePublished, initialP2PNetworkDataReceived, - (state, warning, numPeers, hiddenService, dataReceived) -> { - String result = ""; - int peers = (int) numPeers; - if (warning != null && peers == 0) { - result = warning; - } else { - if (dataReceived && hiddenService) - result = "P2P network peers: " + numPeers; - else if (peers == 0) - result = state; - else - result = state + " / P2P network peers: " + numPeers; - } - return result; - }); - p2PNetworkInfoBinding.subscribe((observable, oldValue, newValue) -> { - p2PNetworkInfo.set(newValue); - }); - - bootstrapState.set("Connecting to Tor network..."); - - p2PService.getNetworkNode().addConnectionListener(new ConnectionListener() { - @Override - public void onConnection(Connection connection) { - } - - @Override - public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection connection) { - // We only check at seed nodes as they are running the latest version - // Other disconnects might be caused by peers running an older version - if (connection.getPeerType() == Connection.PeerType.SEED_NODE && - closeConnectionReason == CloseConnectionReason.RULE_VIOLATION) { - log.warn("RULE_VIOLATION onDisconnect closeConnectionReason=" + closeConnectionReason); - log.warn("RULE_VIOLATION onDisconnect connection=" + connection); - //TODO - /* new Popup() - .warning("You got disconnected from a seed node.\n\n" + - "Reason for getting disconnected: " + connection.getRuleViolation().name() + "\n\n" + - "It might be that your installed version is not compatible with " + - "the network.\n\n" + - "Please check if you run the latest software version.\n" + - "You can download the latest version of Bitsquare at:\n" + - "https://github.com/bitsquare/bitsquare/releases") - .show();*/ - } - } - - @Override - public void onError(Throwable throwable) { - } - }); - - final BooleanProperty p2pNetworkInitialized = new SimpleBooleanProperty(); - p2PService.start(new P2PServiceListener() { - @Override - public void onTorNodeReady() { - bootstrapState.set("Tor node created"); - p2PNetworkIconId.set("image-connection-tor"); - - if (preferences.getUseTorForBitcoinJ()) - initWalletService(); - } - - @Override - public void onHiddenServicePublished() { - hiddenServicePublished.set(true); - bootstrapState.set("Hidden Service published"); - } - - @Override - public void onRequestingDataCompleted() { - initialP2PNetworkDataReceived.set(true); - bootstrapState.set("Initial data received"); - splashP2PNetworkAnimationVisible.set(false); - p2pNetworkInitialized.set(true); - } - - @Override - public void onNoSeedNodeAvailable() { - if (p2PService.getNumConnectedPeers().get() == 0) - bootstrapWarning.set("No seed nodes available"); - else - bootstrapWarning.set(null); - - splashP2PNetworkAnimationVisible.set(false); - p2pNetworkInitialized.set(true); - } - - @Override - public void onNoPeersAvailable() { - if (p2PService.getNumConnectedPeers().get() == 0) { - p2pNetworkWarnMsg.set("There are no seed nodes or persisted peers available for requesting data.\n" + - "Please check your internet connection or try to restart the application."); - bootstrapWarning.set("No seed nodes and peers available"); - p2pNetworkLabelId.set("splash-error-state-msg"); - } else { - bootstrapWarning.set(null); - p2pNetworkLabelId.set("footer-pane"); - } - splashP2PNetworkAnimationVisible.set(false); - p2pNetworkInitialized.set(true); - } - - @Override - public void onBootstrapComplete() { - splashP2PNetworkAnimationVisible.set(false); - bootstrapComplete.set(true); - } - - @Override - public void onSetupFailed(Throwable throwable) { - p2pNetworkWarnMsg.set("Connecting to the P2P network failed (reported error: " - + throwable.getMessage() + ").\n" + - "Please check your internet connection or try to restart the application."); - splashP2PNetworkAnimationVisible.set(false); - bootstrapWarning.set("Bootstrapping to P2P network failed"); - p2pNetworkLabelId.set("splash-error-state-msg"); - } - }); - - return p2pNetworkInitialized; - } - - private void initBitcoinWallet() { - Log.traceCall(); - - // 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(); - } - - private void initWalletService() { - Log.traceCall(); - ObjectProperty walletServiceException = new SimpleObjectProperty<>(); - btcInfoBinding = EasyBind.combine(walletService.downloadPercentageProperty(), walletService.numPeersProperty(), walletServiceException, - (downloadPercentage, numPeers, exception) -> { - String result = ""; - if (exception == null) { - double percentage = (double) downloadPercentage; - int peers = (int) numPeers; - String numPeersString = "Bitcoin network peers: " + peers; - - btcSyncProgress.set(percentage); - if (percentage == 1) { - result = numPeersString + " / synchronized with " + btcNetworkAsString; - btcSplashSyncIconId.set("image-connection-synced"); - } else if (percentage > 0.0) { - result = numPeersString + " / synchronizing with " + btcNetworkAsString + ": " + formatter.formatToPercentWithSymbol(percentage); - } else { - result = numPeersString + " / connecting to " + btcNetworkAsString; - } - } else { - result = "Bitcoin network peers: " + numBtcPeers + " / connecting to " + btcNetworkAsString + " failed"; - if (exception instanceof TimeoutException) { - walletServiceErrorMsg.set("Connecting to the bitcoin network failed because of a timeout."); - } else if (exception.getCause() instanceof BlockStoreException) { - log.error(exception.getMessage()); - // Ugly, but no other way to cover that specific case - if (exception.getMessage().equals("Store file is already locked by another process")) - new Popup().warning("Bitsquare is already running. You cannot run two instances of Bitsquare.") - .closeButtonText("Shut down") - .onClose(BitsquareApp.shutDownHandler::run) - .show(); - else - new Popup().error("Cannot open wallet because of an exception:\n" + exception.getMessage()) - .show(); - } else if (exception.getMessage() != null) { - walletServiceErrorMsg.set("Connection to the bitcoin network failed because of an error:" + exception.getMessage()); - } else { - walletServiceErrorMsg.set("Connection to the bitcoin network failed because of an error:" + exception.toString()); - } - } - return result; - - }); - btcInfoBinding.subscribe((observable, oldValue, newValue) -> { - btcInfo.set(newValue); - }); - - walletService.initialize(null, - () -> { - numBtcPeers = walletService.numPeersProperty().get(); - - if (walletService.getWallet().isEncrypted()) { - if (p2pNetWorkReady.get()) - splashP2PNetworkAnimationVisible.set(false); - - walletPasswordWindow - .onAesKey(aesKey -> { - walletService.setAesKey(aesKey); - tradeWalletService.setAesKey(aesKey); - walletInitialized.set(true); - }) - .hideCloseButton() - .show(); - } else { - walletInitialized.set(true); - } - }, - walletServiceException::set); - } - - private void onAllServicesInitialized() { - Log.traceCall(); - - clock.start(); - - // disputeManager - disputeManager.onAllServicesInitialized(); - disputeManager.getDisputesAsObservableList().addListener((ListChangeListener) change -> { - change.next(); - onDisputesChangeListener(change.getAddedSubList(), change.getRemoved()); - }); - onDisputesChangeListener(disputeManager.getDisputesAsObservableList(), null); - - // tradeManager - tradeManager.onAllServicesInitialized(); - tradeManager.getTrades().addListener((ListChangeListener) c -> updateBalance()); - tradeManager.getTrades().addListener((ListChangeListener) change -> onTradesChanged()); - onTradesChanged(); - // We handle the trade period here as we display a global popup if we reached dispute time - tradesAndUIReady = EasyBind.combine(isSplashScreenRemoved, tradeManager.pendingTradesInitializedProperty(), (a, b) -> a && b); - tradesAndUIReady.subscribe((observable, oldValue, newValue) -> { - if (newValue) - applyTradePeriodState(); - }); - - // walletService - walletService.addBalanceListener(new BalanceListener() { - @Override - public void onBalanceChanged(Coin balance, Transaction tx) { - updateBalance(); - } - }); - - openOfferManager.getOpenOffers().addListener((ListChangeListener) c -> updateBalance()); - tradeManager.getTrades().addListener((ListChangeListener) c -> updateBalance()); - openOfferManager.onAllServicesInitialized(); - arbitratorManager.onAllServicesInitialized(); - alertManager.alertMessageProperty().addListener((observable, oldValue, newValue) -> displayAlertIfPresent(newValue)); - privateNotificationManager.privateNotificationProperty().addListener((observable, oldValue, newValue) -> displayPrivateNotification(newValue)); - displayAlertIfPresent(alertManager.alertMessageProperty().get()); - - p2PService.onAllServicesInitialized(); - - setupBtcNumPeersWatcher(); - setupP2PNumPeersWatcher(); - updateBalance(); - if (DevFlags.DEV_MODE) { - preferences.setShowOwnOffersInOfferBook(true); - if (user.getPaymentAccounts().isEmpty()) - setupDevDummyPaymentAccounts(); - } - setupMarketPriceFeed(); - swapPendingOfferFundingEntries(); - fillPriceFeedComboBoxItems(); - - showAppScreen.set(true); - - - // We want to test if the client is compiled with the correct crypto provider (BountyCastle) - // and if the unlimited Strength for cryptographic keys is set. - // If users compile themselves they might miss that step and then would get an exception in the trade. - // To avoid that we add here at startup a sample encryption and signing to see if it don't causes an exception. - // See: https://github.com/bitsquare/bitsquare/blob/master/doc/build.md#7-enable-unlimited-strength-for-cryptographic-keys - Thread checkCryptoThread = new Thread() { - @Override - public void run() { - try { - Thread.currentThread().setName("checkCryptoThread"); - log.trace("Run crypto test"); - // just use any simple dummy msg - io.bitsquare.p2p.peers.keepalive.messages.Ping payload = new Ping(1, 1); - SealedAndSigned sealedAndSigned = Encryption.encryptHybridWithSignature(payload, - keyRing.getSignatureKeyPair(), keyRing.getPubKeyRing().getEncryptionPubKey()); - DecryptedDataTuple tuple = Encryption.decryptHybridWithSignature(sealedAndSigned, keyRing.getEncryptionKeyPair().getPrivate()); - if (tuple.payload instanceof Ping && - ((Ping) tuple.payload).nonce == payload.nonce && - ((Ping) tuple.payload).lastRoundTripTime == payload.lastRoundTripTime) - log.debug("Crypto test succeeded"); - else - throw new CryptoException("Payload not correct after decryption"); - } catch (CryptoException e) { - e.printStackTrace(); - String msg = "Seems that you use a self compiled binary and have not following the build " + - "instructions in https://github.com/bitsquare/bitsquare/blob/master/doc/build.md#7-enable-unlimited-strength-for-cryptographic-keys.\n\n" + - "If that is not the case and you use the official Bitsquare binary, " + - "please file a bug report to the Github page.\n" + - "Error=" + e.getMessage(); - log.error(msg); - UserThread.execute(() -> new Popup<>().warning(msg) - .actionButtonText("Shut down") - .onAction(BitsquareApp.shutDownHandler::run) - .closeButtonText("Report bug at Github issues") - .onClose(() -> GUIUtil.openWebPage("https://github.com/bitsquare/bitsquare/issues")) - .show()); - } - } - }; - checkCryptoThread.start(); - - if (Security.getProvider("BC") == null) { - new Popup<>().warning("There is a problem with the crypto libraries. BountyCastle is not available.") - .actionButtonText("Shut down") - .onAction(BitsquareApp.shutDownHandler::run) - .closeButtonText("Report bug at Github issues") - .onClose(() -> GUIUtil.openWebPage("https://github.com/bitsquare/bitsquare/issues")) - .show(); - } - - String remindPasswordAndBackupKey = "remindPasswordAndBackup"; - user.getPaymentAccountsAsObservable().addListener((SetChangeListener) change -> { - if (!walletService.getWallet().isEncrypted() && preferences.showAgain(remindPasswordAndBackupKey) && change.wasAdded()) { - new Popup<>().headLine("Important security recommendation") - .information("We would like to remind you to consider using password protection for your wallet if you have not already enabled that.\n\n" + - "It is also highly recommended to write down the wallet seed words. Those seed words are like a master password for recovering your Bitcoin wallet.\n" + - "At the \"Wallet Seed\" section you find more information.\n\n" + - "Additionally you can backup the complete application data folder at the \"Backup\" section.\n" + - "Please note, that this backup is not encrypted!") - .dontShowAgainId(remindPasswordAndBackupKey, preferences) - .show(); - } - }); - - checkIfOpenOffersMatchTradeProtocolVersion(); - } - - private void checkIfOpenOffersMatchTradeProtocolVersion() { - List outDatedOffers = openOfferManager.getOpenOffers() - .stream() - .filter(e -> e.getOffer().getProtocolVersion() != Version.TRADE_PROTOCOL_VERSION) - .collect(Collectors.toList()); - if (!outDatedOffers.isEmpty()) { - new Popup<>() - .warning("You have open offers which have been created with an older version of Bitsquare.\n" + - "Please remove those offers as they are not valid anymore.\n\n" + - "Offers (ID): " + - outDatedOffers.stream() - .map(e -> e.getId() + "\n") - .collect(Collectors.toList()).toString() - .replace("[", "").replace("]", "")) - .actionButtonText("Remove outdated offer(s)") - .onAction(() -> openOfferManager.removeOpenOffers(outDatedOffers, null)) - .closeButtonText("Shut down") - .onClose(BitsquareApp.shutDownHandler::run) - .show(); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // UI handlers - /////////////////////////////////////////////////////////////////////////////////////////// - - // After showAppScreen is set and splash screen is faded out - void onSplashScreenRemoved() { - isSplashScreenRemoved.set(true); - - // Delay that as we want to know what is the current path of the navigation which is set - // in MainView showAppScreen handler - notificationCenter.onAllServicesAndViewsInitialized(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // States - /////////////////////////////////////////////////////////////////////////////////////////// - - private void applyTradePeriodState() { - updateTradePeriodState(); - clock.addListener(new Clock.Listener() { - @Override - public void onSecondTick() { - } - - @Override - public void onMinuteTick() { - updateTradePeriodState(); - } - - @Override - public void onMissedSecondTick(long missed) { - } - }); - } - - private void updateTradePeriodState() { - tradeManager.getTrades().stream().forEach(trade -> { - if (trade.getState().getPhase().ordinal() < Trade.Phase.PAYOUT_PAID.ordinal()) { - Date maxTradePeriodDate = trade.getMaxTradePeriodDate(); - Date halfTradePeriodDate = trade.getHalfTradePeriodDate(); - if (maxTradePeriodDate != null && halfTradePeriodDate != null) { - Date now = new Date(); - if (now.after(maxTradePeriodDate)) - trade.setTradePeriodState(Trade.TradePeriodState.TRADE_PERIOD_OVER); - else if (now.after(halfTradePeriodDate)) - trade.setTradePeriodState(Trade.TradePeriodState.HALF_REACHED); - - String key; - switch (trade.getTradePeriodState()) { - case NORMAL: - break; - case HALF_REACHED: - key = "displayHalfTradePeriodOver" + trade.getId(); - if (preferences.showAgain(key)) { - preferences.dontShowAgain(key, true); - new Popup().warning("Your trade with ID " + trade.getShortId() + - " has reached the half of the max. allowed trading period and " + - "is still not completed.\n\n" + - "The trade period ends on " + formatter.formatDateTime(maxTradePeriodDate) + "\n\n" + - "Please check your trade state at \"Portfolio/Open trades\" for further information.") - .show(); - } - break; - case TRADE_PERIOD_OVER: - key = "displayTradePeriodOver" + trade.getId(); - if (preferences.showAgain(key)) { - preferences.dontShowAgain(key, true); - new Popup().warning("Your trade with ID " + trade.getShortId() + - " has reached the max. allowed trading period and is " + - "not completed.\n\n" + - "The trade period ended on " + formatter.formatDateTime(maxTradePeriodDate) + "\n\n" + - "Please check your trade at \"Portfolio/Open trades\" for contacting " + - "the arbitrator.") - .show(); - } - break; - } - } - } - }); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void setupP2PNumPeersWatcher() { - p2PService.getNumConnectedPeers().addListener((observable, oldValue, newValue) -> { - int numPeers = (int) newValue; - if ((int) oldValue > 0 && numPeers == 0) { - // give a bit of tolerance - if (checkNumberOfP2pNetworkPeersTimer != null) - checkNumberOfP2pNetworkPeersTimer.stop(); - - checkNumberOfP2pNetworkPeersTimer = UserThread.runAfter(() -> { - // check again numPeers - if (p2PService.getNumConnectedPeers().get() == 0) { - p2pNetworkWarnMsg.set("You lost the connection to all P2P network peers.\n" + - "Maybe you lost your internet connection or your computer was in standby mode."); - p2pNetworkLabelId.set("splash-error-state-msg"); - } else { - p2pNetworkWarnMsg.set(null); - p2pNetworkLabelId.set("footer-pane"); - } - }, 5); - } else if ((int) oldValue == 0 && numPeers > 0) { - if (checkNumberOfP2pNetworkPeersTimer != null) - checkNumberOfP2pNetworkPeersTimer.stop(); - - p2pNetworkWarnMsg.set(null); - p2pNetworkLabelId.set("footer-pane"); - } - }); - } - - private void setupBtcNumPeersWatcher() { - walletService.numPeersProperty().addListener((observable, oldValue, newValue) -> { - int numPeers = (int) newValue; - if ((int) oldValue > 0 && numPeers == 0) { - if (checkNumberOfBtcPeersTimer != null) - checkNumberOfBtcPeersTimer.stop(); - - checkNumberOfBtcPeersTimer = UserThread.runAfter(() -> { - // check again numPeers - if (walletService.numPeersProperty().get() == 0) { - walletServiceErrorMsg.set("You lost the connection to all bitcoin network peers.\n" + - "Maybe you lost your internet connection or your computer was in standby mode."); - } else { - walletServiceErrorMsg.set(null); - } - }, 5); - } else if ((int) oldValue == 0 && numPeers > 0) { - if (checkNumberOfBtcPeersTimer != null) - checkNumberOfBtcPeersTimer.stop(); - walletServiceErrorMsg.set(null); - } - }); - } - - private void setupMarketPriceFeed() { - if (priceFeedService.getCurrencyCode() == null) - priceFeedService.setCurrencyCode(preferences.getPreferredTradeCurrency().getCode()); - if (priceFeedService.getType() == null) - priceFeedService.setType(PriceFeedService.Type.LAST); - priceFeedService.init(price -> marketPrice.set(formatter.formatMarketPrice(price, priceFeedService.getCurrencyCode())), - (errorMessage, throwable) -> marketPrice.set("N/A")); - marketPriceCurrencyCode.bind(priceFeedService.currencyCodeProperty()); - typeProperty.bind(priceFeedService.typeProperty()); - - marketPriceBinding = EasyBind.combine( - marketPriceCurrencyCode, marketPrice, - (currencyCode, price) -> formatter.getCurrencyPair(currencyCode) + ": " + price); - - marketPriceBinding.subscribe((observable, oldValue, newValue) -> { - if (newValue != null && !newValue.equals(oldValue)) { - setMarketPriceInItems(); - - String code = preferences.getUseStickyMarketPrice() ? - preferences.getPreferredTradeCurrency().getCode() : - priceFeedService.currencyCodeProperty().get(); - Optional itemOptional = findPriceFeedComboBoxItem(code); - if (itemOptional.isPresent()) { - if (selectedPriceFeedComboBoxItemProperty.get() == null || !preferences.getUseStickyMarketPrice()) { - itemOptional.get().setDisplayString(newValue); - selectedPriceFeedComboBoxItemProperty.set(itemOptional.get()); - } - } else { - if (CurrencyUtil.isCryptoCurrency(code)) { - CurrencyUtil.getCryptoCurrency(code).ifPresent(cryptoCurrency -> { - preferences.addCryptoCurrency(cryptoCurrency); - fillPriceFeedComboBoxItems(); - }); - } else { - CurrencyUtil.getFiatCurrency(code).ifPresent(fiatCurrency -> { - preferences.addFiatCurrency(fiatCurrency); - fillPriceFeedComboBoxItems(); - }); - } - } - - if (selectedPriceFeedComboBoxItemProperty.get() != null) - selectedPriceFeedComboBoxItemProperty.get().setDisplayString(newValue); - } - }); - - priceFeedAllLoadedSubscription = EasyBind.subscribe(priceFeedService.currenciesUpdateFlagProperty(), newPriceUpdate -> setMarketPriceInItems()); - - preferences.getTradeCurrenciesAsObservable().addListener((ListChangeListener) c -> { - UserThread.runAfter(() -> { - fillPriceFeedComboBoxItems(); - setMarketPriceInItems(); - }, 100, TimeUnit.MILLISECONDS); - }); - } - - private void setMarketPriceInItems() { - priceFeedComboBoxItems.stream().forEach(item -> { - String currencyCode = item.currencyCode; - MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode); - String priceString; - if (marketPrice != null) { - double price = marketPrice.getPrice(priceFeedService.getType()); - if (price != 0) { - priceString = formatter.formatMarketPrice(price, currencyCode); - item.setIsPriceAvailable(true); - } else { - priceString = "N/A"; - item.setIsPriceAvailable(false); - } - } else { - priceString = "N/A"; - item.setIsPriceAvailable(false); - } - item.setDisplayString(formatter.getCurrencyPair(currencyCode) + ": " + priceString); - }); - } - - public void setPriceFeedComboBoxItem(PriceFeedComboBoxItem item) { - if (!preferences.getUseStickyMarketPrice() && item != null) { - Optional itemOptional = findPriceFeedComboBoxItem(priceFeedService.currencyCodeProperty().get()); - if (itemOptional.isPresent()) - selectedPriceFeedComboBoxItemProperty.set(itemOptional.get()); - else - findPriceFeedComboBoxItem(preferences.getPreferredTradeCurrency().getCode()) - .ifPresent(item2 -> selectedPriceFeedComboBoxItemProperty.set(item2)); - - priceFeedService.setCurrencyCode(item.currencyCode); - } else if (item != null) { - selectedPriceFeedComboBoxItemProperty.set(item); - priceFeedService.setCurrencyCode(item.currencyCode); - } else { - findPriceFeedComboBoxItem(preferences.getPreferredTradeCurrency().getCode()) - .ifPresent(item2 -> selectedPriceFeedComboBoxItemProperty.set(item2)); - } - - // Need a delay a bit as we get item.isPriceAvailable() set after that call. - // (In case we add a new currency in settings) - UserThread.runAfter(() -> { - if (item != null) { - String code = item.currencyCode; - isFiatCurrencyPriceFeedSelected.set(CurrencyUtil.isFiatCurrency(code) && CurrencyUtil.getFiatCurrency(code).isPresent() && item.isPriceAvailable()); - isCryptoCurrencyPriceFeedSelected.set(CurrencyUtil.isCryptoCurrency(code) && CurrencyUtil.getCryptoCurrency(code).isPresent() && item.isPriceAvailable()); - } - }, 100, TimeUnit.MILLISECONDS); - } - - Optional findPriceFeedComboBoxItem(String currencyCode) { - return priceFeedComboBoxItems.stream() - .filter(item -> item.currencyCode.equals(currencyCode)) - .findAny(); - } - - private void fillPriceFeedComboBoxItems() { - List currencyItems = preferences.getTradeCurrenciesAsObservable() - .stream() - .map(tradeCurrency -> new PriceFeedComboBoxItem(tradeCurrency.getCode())) - .collect(Collectors.toList()); - priceFeedComboBoxItems.setAll(currencyItems); - } - - private void displayAlertIfPresent(Alert alert) { - boolean alreadyDisplayed = alert != null && alert.equals(user.getDisplayedAlert()); - user.setDisplayedAlert(alert); - if (alert != null && !alreadyDisplayed) - if (alert.isUpdateInfo || alert.isNewVersion()) - new DisplayUpdateDownloadWindow().alertMessage(alert).show(); - else - new DisplayAlertMessageWindow().alertMessage(alert).show(); - } - - private void displayPrivateNotification(PrivateNotification privateNotification) { - new Popup<>().headLine("Important private notification!") - .attention(privateNotification.message) - .setHeadlineStyle("-fx-text-fill: -bs-error-red; -fx-font-weight: bold; -fx-font-size: 16;") - .onClose(() -> privateNotificationManager.removePrivateNotification()) - .closeButtonText("I understand") - .show(); - } - - private void swapPendingOfferFundingEntries() { - tradeManager.getAddressEntriesForAvailableBalanceStream() - .filter(addressEntry -> addressEntry.getOfferId() != null) - .forEach(addressEntry -> walletService.swapTradeEntryToAvailableEntry(addressEntry.getOfferId(), AddressEntry.Context.OFFER_FUNDING)); - } - - private void updateBalance() { - // Without delaying to the next cycle it does not update. - // Seems order of events we are listening on causes that... - UserThread.execute(() -> { - updateAvailableBalance(); - updateReservedBalance(); - updateLockedBalance(); - }); - } - - private void updateAvailableBalance() { - Coin totalAvailableBalance = Coin.valueOf(tradeManager.getAddressEntriesForAvailableBalanceStream() - .mapToLong(addressEntry -> walletService.getBalanceForAddress(addressEntry.getAddress()).getValue()) - .sum()); - availableBalance.set(formatter.formatCoinWithCode(totalAvailableBalance)); - } - - private void updateReservedBalance() { - Coin sum = Coin.valueOf(openOfferManager.getOpenOffers().stream() - .map(openOffer -> { - Address address = walletService.getOrCreateAddressEntry(openOffer.getId(), AddressEntry.Context.RESERVED_FOR_TRADE).getAddress(); - return walletService.getBalanceForAddress(address); - }) - .mapToLong(Coin::getValue) - .sum()); - - reservedBalance.set(formatter.formatCoinWithCode(sum)); - } - - private void updateLockedBalance() { - Coin sum = Coin.valueOf(tradeManager.getLockedTradeStream() - .mapToLong(trade -> { - Coin lockedTradeAmount = walletService.getOrCreateAddressEntry(trade.getId(), AddressEntry.Context.MULTI_SIG).getCoinLockedInMultiSig(); - return lockedTradeAmount != null ? lockedTradeAmount.getValue() : 0; - }) - .sum()); - lockedBalance.set(formatter.formatCoinWithCode(sum)); - } - - private void onDisputesChangeListener(List addedList, @Nullable List removedList) { - if (removedList != null) { - removedList.stream().forEach(dispute -> { - String id = dispute.getId(); - if (disputeIsClosedSubscriptionsMap.containsKey(id)) { - disputeIsClosedSubscriptionsMap.get(id).unsubscribe(); - disputeIsClosedSubscriptionsMap.remove(id); - } - }); - } - addedList.stream().forEach(dispute -> { - String id = dispute.getId(); - Subscription disputeStateSubscription = EasyBind.subscribe(dispute.isClosedProperty(), - isClosed -> { - // We get event before list gets updated, so we execute on next frame - UserThread.execute(() -> { - int openDisputes = disputeManager.getDisputesAsObservableList().stream() - .filter(e -> !e.isClosed()) - .collect(Collectors.toList()).size(); - if (openDisputes > 0) - numOpenDisputesAsString.set(String.valueOf(openDisputes)); - if (openDisputes > 9) - numOpenDisputesAsString.set("★"); - - showOpenDisputesNotification.set(openDisputes > 0); - }); - }); - disputeIsClosedSubscriptionsMap.put(id, disputeStateSubscription); - }); - } - - private void onTradesChanged() { - long numPendingTrades = tradeManager.getTrades().size(); - if (numPendingTrades > 0) - numPendingTradesAsString.set(String.valueOf(numPendingTrades)); - if (numPendingTrades > 9) - numPendingTradesAsString.set("★"); - - showPendingTradesNotification.set(numPendingTrades > 0); - } - - private void setupDevDummyPaymentAccounts() { - OKPayAccount okPayAccount = new OKPayAccount(); - okPayAccount.setAccountNr("dummy_" + new Random().nextInt(100)); - okPayAccount.setAccountName("OKPay dummy"); - okPayAccount.setSelectedTradeCurrency(CurrencyUtil.getDefaultTradeCurrency()); - user.addPaymentAccount(okPayAccount); - - CryptoCurrencyAccount cryptoCurrencyAccount = new CryptoCurrencyAccount(); - cryptoCurrencyAccount.setAccountName("ETH dummy"); - cryptoCurrencyAccount.setAddress("0x" + new Random().nextInt(1000000)); - cryptoCurrencyAccount.setSingleTradeCurrency(CurrencyUtil.getCryptoCurrency("ETH").get()); - user.addPaymentAccount(cryptoCurrencyAccount); - } -} diff --git a/pom.xml b/pom.xml index 167affd557..a309f5a2a5 100644 --- a/pom.xml +++ b/pom.xml @@ -133,7 +133,7 @@ bcprov-jdk15on 1.56 - + --> org.bouncycastle