diff --git a/bootstrap/src/main/java/io/bitsquare/p2p/seed/SeedNodeMain.java b/bootstrap/src/main/java/io/bitsquare/p2p/seed/SeedNodeMain.java
new file mode 100644
index 0000000000..8d722278ea
--- /dev/null
+++ b/bootstrap/src/main/java/io/bitsquare/p2p/seed/SeedNodeMain.java
@@ -0,0 +1,16 @@
+package io.bitsquare.p2p.seed;
+
+import java.security.NoSuchAlgorithmException;
+
+public class SeedNodeMain {
+
+ // args: port useLocalhost seedNodes
+ // eg. 4444 true localhost:7777 localhost:8888
+ // To stop enter: q
+ public static void main(String[] args) throws NoSuchAlgorithmException {
+ SeedNode seedNode = new SeedNode();
+ seedNode.processArgs(args);
+ seedNode.createAndStartP2PService();
+ seedNode.listenForExitCommand();
+ }
+}
diff --git a/common/pom.xml b/common/pom.xml
new file mode 100644
index 0000000000..82a4d7df32
--- /dev/null
+++ b/common/pom.xml
@@ -0,0 +1,28 @@
+
+
+
+ parent
+ io.bitsquare
+ 0.3.2-SNAPSHOT
+
+ 4.0.0
+
+ common
+
+
+
+ com.google.code.gson
+ gson
+ 2.2.4
+
+
+
+ org.springframework
+ spring-core
+ 4.1.1.RELEASE
+
+
+
+
\ No newline at end of file
diff --git a/common/src/main/java/io/bitsquare/app/AppModule.java b/common/src/main/java/io/bitsquare/app/AppModule.java
new file mode 100644
index 0000000000..9965d80bce
--- /dev/null
+++ b/common/src/main/java/io/bitsquare/app/AppModule.java
@@ -0,0 +1,65 @@
+/*
+ * 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.app;
+
+import com.google.common.base.Preconditions;
+import com.google.inject.AbstractModule;
+import com.google.inject.Injector;
+import org.springframework.core.env.Environment;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class AppModule extends AbstractModule {
+ protected final Environment env;
+
+ private final List modules = new ArrayList<>();
+
+ protected AppModule(Environment env) {
+ Preconditions.checkNotNull(env, "Environment must not be null");
+ this.env = env;
+ }
+
+ protected void install(AppModule module) {
+ super.install(module);
+ modules.add(module);
+ }
+
+ /**
+ * Close any instances this module is responsible for and recursively close any
+ * sub-modules installed via {@link #install(AppModule)}. This method
+ * must be called manually, e.g. at the end of a main() method or in the stop() method
+ * of a JavaFX Application; alternatively it may be registered as a JVM shutdown hook.
+ *
+ * @param injector the Injector originally initialized with this module
+ * @see #doClose(com.google.inject.Injector)
+ */
+ public final void close(Injector injector) {
+ modules.forEach(module -> module.close(injector));
+ doClose(injector);
+ }
+
+ /**
+ * Actually perform closing of any instances this module is responsible for. Called by
+ * {@link #close(Injector)}.
+ *
+ * @param injector the Injector originally initialized with this module
+ */
+ protected void doClose(Injector injector) {
+ }
+}
diff --git a/common/src/main/java/io/bitsquare/app/ProgramArguments.java b/common/src/main/java/io/bitsquare/app/ProgramArguments.java
new file mode 100644
index 0000000000..c2a12dd6b0
--- /dev/null
+++ b/common/src/main/java/io/bitsquare/app/ProgramArguments.java
@@ -0,0 +1,19 @@
+package io.bitsquare.app;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+// TODO too app specific for common...
+public class ProgramArguments {
+ // program arg names
+ public static final String TOR_DIR = "torDir";
+ public static final String USE_LOCALHOST = "useLocalhost";
+ public static final String DEV_TEST = "devTest";
+
+
+ public static final String NAME_KEY = "node.name";
+ public static final String PORT_KEY = "node.port";
+
+
+ private static final Logger log = LoggerFactory.getLogger(ProgramArguments.class);
+}
diff --git a/common/src/main/java/io/bitsquare/app/Version.java b/common/src/main/java/io/bitsquare/app/Version.java
new file mode 100644
index 0000000000..1ad878648e
--- /dev/null
+++ b/common/src/main/java/io/bitsquare/app/Version.java
@@ -0,0 +1,45 @@
+/*
+ * 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.app;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Version {
+ private static final Logger log = LoggerFactory.getLogger(Version.class);
+
+ // The application versions
+ private static final int MAJOR_VERSION = 0;
+ private static final int MINOR_VERSION = 3;
+ // used as updateFX index
+ public static final int PATCH_VERSION = 2;
+
+ public static final String VERSION = MAJOR_VERSION + "." + MINOR_VERSION + "." + PATCH_VERSION;
+
+ // The version nr. for the objects sent over the network. A change will break the serialization of old objects.
+ // If objects are used for both network and database the network version is applied.
+ public static final long NETWORK_PROTOCOL_VERSION = 1;
+
+ // The version nr. of the serialized data stored to disc. A change will break the serialization of old objects.
+ public static final long LOCAL_DB_VERSION = 1;
+
+ // The version nr. of the current protocol. The offer holds that version. A taker will check the version of the offers to see if he his version is
+ // compatible.
+ public static final long PROTOCOL_VERSION = 1;
+
+}
diff --git a/common/src/main/java/io/bitsquare/common/ByteArrayUtils.java b/common/src/main/java/io/bitsquare/common/ByteArrayUtils.java
new file mode 100644
index 0000000000..d9478ede8d
--- /dev/null
+++ b/common/src/main/java/io/bitsquare/common/ByteArrayUtils.java
@@ -0,0 +1,81 @@
+/*
+ * 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;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+
+public class ByteArrayUtils {
+ private static final Logger log = LoggerFactory.getLogger(ByteArrayUtils.class);
+ private static long lastTimeStamp = System.currentTimeMillis();
+
+ public static T byteArrayToObject(byte[] data) {
+ ByteArrayInputStream bis = new ByteArrayInputStream(data);
+ ObjectInput in = null;
+ Object result = null;
+ try {
+ in = new ObjectInputStream(bis);
+ result = in.readObject();
+ } 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[] objectToByteArray(Object object) {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutput out = null;
+ byte[] result = null;
+ try {
+ out = new ObjectOutputStream(bos);
+ out.writeObject(object);
+ result = bos.toByteArray();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ if (out != null) {
+ out.close();
+ }
+ } catch (IOException ex) {
+ // ignore close exception
+ }
+ try {
+ bos.close();
+ } catch (IOException ex) {
+ // ignore close exception
+ }
+ }
+ return result;
+ }
+}
diff --git a/common/src/main/java/io/bitsquare/common/UserThread.java b/common/src/main/java/io/bitsquare/common/UserThread.java
new file mode 100644
index 0000000000..bce70d270b
--- /dev/null
+++ b/common/src/main/java/io/bitsquare/common/UserThread.java
@@ -0,0 +1,38 @@
+/*
+ * 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;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+public class UserThread {
+
+ public static Executor getExecutor() {
+ return executor;
+ }
+
+ public static void setExecutor(Executor executor) {
+ UserThread.executor = executor;
+ }
+
+ public static Executor executor = Executors.newSingleThreadExecutor();
+
+ public static void execute(Runnable command) {
+ UserThread.executor.execute(command);
+ }
+}
diff --git a/common/src/main/java/io/bitsquare/common/handlers/ErrorMessageHandler.java b/common/src/main/java/io/bitsquare/common/handlers/ErrorMessageHandler.java
new file mode 100644
index 0000000000..b9972f2913
--- /dev/null
+++ b/common/src/main/java/io/bitsquare/common/handlers/ErrorMessageHandler.java
@@ -0,0 +1,25 @@
+/*
+ * 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.handlers;
+
+/**
+ * For reporting error message only (UI)
+ */
+public interface ErrorMessageHandler {
+ void handleErrorMessage(String errorMessage);
+}
diff --git a/common/src/main/java/io/bitsquare/common/handlers/ExceptionHandler.java b/common/src/main/java/io/bitsquare/common/handlers/ExceptionHandler.java
new file mode 100644
index 0000000000..c1122c1236
--- /dev/null
+++ b/common/src/main/java/io/bitsquare/common/handlers/ExceptionHandler.java
@@ -0,0 +1,25 @@
+/*
+ * 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.handlers;
+
+/**
+ * For reporting throwable objects only
+ */
+public interface ExceptionHandler {
+ void handleException(Throwable throwable);
+}
diff --git a/common/src/main/java/io/bitsquare/common/handlers/FaultHandler.java b/common/src/main/java/io/bitsquare/common/handlers/FaultHandler.java
new file mode 100644
index 0000000000..3d2913dfa5
--- /dev/null
+++ b/common/src/main/java/io/bitsquare/common/handlers/FaultHandler.java
@@ -0,0 +1,25 @@
+/*
+ * 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.handlers;
+
+/**
+ * For reporting a description message and throwable
+ */
+public interface FaultHandler {
+ void handleFault(String errorMessage, Throwable throwable);
+}
diff --git a/common/src/main/java/io/bitsquare/common/handlers/ResultHandler.java b/common/src/main/java/io/bitsquare/common/handlers/ResultHandler.java
new file mode 100644
index 0000000000..60ac05be77
--- /dev/null
+++ b/common/src/main/java/io/bitsquare/common/handlers/ResultHandler.java
@@ -0,0 +1,22 @@
+/*
+ * 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.handlers;
+
+public interface ResultHandler extends Runnable {
+ void handleResult();
+}
diff --git a/common/src/main/java/io/bitsquare/common/taskrunner/InterceptTaskException.java b/common/src/main/java/io/bitsquare/common/taskrunner/InterceptTaskException.java
new file mode 100644
index 0000000000..f3f19af231
--- /dev/null
+++ b/common/src/main/java/io/bitsquare/common/taskrunner/InterceptTaskException.java
@@ -0,0 +1,30 @@
+/*
+ * 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.taskrunner;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class InterceptTaskException extends RuntimeException {
+ private static final Logger log = LoggerFactory.getLogger(InterceptTaskException.class);
+ private static final long serialVersionUID = 5216202440370333534L;
+
+ public InterceptTaskException(String message) {
+ super(message);
+ }
+}
diff --git a/common/src/main/java/io/bitsquare/common/taskrunner/Model.java b/common/src/main/java/io/bitsquare/common/taskrunner/Model.java
new file mode 100644
index 0000000000..5c10f6a735
--- /dev/null
+++ b/common/src/main/java/io/bitsquare/common/taskrunner/Model.java
@@ -0,0 +1,24 @@
+/*
+ * 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.taskrunner;
+
+public interface Model {
+ void persist();
+
+ void onComplete();
+}
diff --git a/common/src/main/java/io/bitsquare/common/taskrunner/Task.java b/common/src/main/java/io/bitsquare/common/taskrunner/Task.java
new file mode 100644
index 0000000000..280ff0b940
--- /dev/null
+++ b/common/src/main/java/io/bitsquare/common/taskrunner/Task.java
@@ -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 .
+ */
+
+package io.bitsquare.common.taskrunner;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class Task {
+ private static final Logger log = LoggerFactory.getLogger(Task.class);
+
+ public static Class extends Task> taskToIntercept;
+
+ private final TaskRunner taskHandler;
+ protected final T model;
+ protected String errorMessage = "An error occurred at task: " + getClass().getSimpleName();
+
+ public Task(TaskRunner taskHandler, T model) {
+ this.taskHandler = taskHandler;
+ this.model = model;
+ }
+
+ abstract protected void run();
+
+ protected void runInterceptHook() {
+ if (getClass() == taskToIntercept)
+ throw new InterceptTaskException("Task intercepted for testing purpose. Task = " + getClass().getSimpleName());
+ }
+
+ protected void appendToErrorMessage(String message) {
+ errorMessage += "\n" + message;
+ }
+
+ protected void appendExceptionToErrorMessage(Throwable t) {
+ if (t.getMessage() != null)
+ errorMessage += "\nException message: " + t.getMessage();
+ else
+ errorMessage += "\nException: " + t.toString();
+ }
+
+ protected void complete() {
+ taskHandler.handleComplete();
+ }
+
+ protected void failed(String message) {
+ appendToErrorMessage(message);
+ failed();
+ }
+
+ protected void failed(Throwable t) {
+ t.printStackTrace();
+ appendExceptionToErrorMessage(t);
+ failed();
+ }
+
+ protected void failed() {
+ taskHandler.handleErrorMessage(errorMessage);
+ }
+
+}
diff --git a/common/src/main/java/io/bitsquare/common/taskrunner/TaskRunner.java b/common/src/main/java/io/bitsquare/common/taskrunner/TaskRunner.java
new file mode 100644
index 0000000000..cfba7dd077
--- /dev/null
+++ b/common/src/main/java/io/bitsquare/common/taskrunner/TaskRunner.java
@@ -0,0 +1,95 @@
+/*
+ * 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.taskrunner;
+
+import io.bitsquare.common.handlers.ErrorMessageHandler;
+import io.bitsquare.common.handlers.ResultHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Arrays;
+import java.util.Queue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+public class TaskRunner {
+ private static final Logger log = LoggerFactory.getLogger(TaskRunner.class);
+
+ private final Queue> tasks = new LinkedBlockingQueue<>();
+ private final T sharedModel;
+ private final Class sharedModelClass;
+ private final ResultHandler resultHandler;
+ private final ErrorMessageHandler errorMessageHandler;
+ private boolean failed = false;
+ private boolean isCanceled;
+
+ private Class extends Task> currentTask;
+
+
+ public TaskRunner(T sharedModel, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
+ this(sharedModel, (Class) sharedModel.getClass(), resultHandler, errorMessageHandler);
+ }
+
+ public TaskRunner(T sharedModel, Class sharedModelClass, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
+ this.sharedModel = sharedModel;
+ this.resultHandler = resultHandler;
+ this.errorMessageHandler = errorMessageHandler;
+ this.sharedModelClass = sharedModelClass;
+ }
+
+ public final void addTasks(Class extends Task>... items) {
+ tasks.addAll(Arrays.asList(items));
+ }
+
+ public void run() {
+ next();
+ }
+
+ private void next() {
+ if (!failed && !isCanceled) {
+ if (tasks.size() > 0) {
+ try {
+ currentTask = tasks.poll();
+ log.trace("Run task: " + currentTask.getSimpleName());
+ currentTask.getDeclaredConstructor(TaskRunner.class, sharedModelClass).newInstance(this, sharedModel).run();
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ handleErrorMessage("Error at taskRunner: " + throwable.getMessage());
+ }
+ } else {
+ resultHandler.handleResult();
+ }
+ }
+ }
+
+ public void cancel() {
+ isCanceled = true;
+ }
+
+ void handleComplete() {
+ log.trace("Task completed: " + currentTask.getSimpleName());
+ sharedModel.persist();
+ next();
+ }
+
+ void handleErrorMessage(String errorMessage) {
+ log.error("Task failed: " + currentTask.getSimpleName());
+ log.error("errorMessage: " + errorMessage);
+ failed = true;
+ errorMessageHandler.handleErrorMessage(errorMessage);
+ }
+}
diff --git a/common/src/main/java/io/bitsquare/common/util/JsonExclude.java b/common/src/main/java/io/bitsquare/common/util/JsonExclude.java
new file mode 100644
index 0000000000..a30483e8c6
--- /dev/null
+++ b/common/src/main/java/io/bitsquare/common/util/JsonExclude.java
@@ -0,0 +1,28 @@
+/*
+ * 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 java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface JsonExclude {
+}
\ No newline at end of file
diff --git a/common/src/main/java/io/bitsquare/common/util/Profiler.java b/common/src/main/java/io/bitsquare/common/util/Profiler.java
new file mode 100644
index 0000000000..fc041446bf
--- /dev/null
+++ b/common/src/main/java/io/bitsquare/common/util/Profiler.java
@@ -0,0 +1,34 @@
+/*
+ * 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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Profiler {
+ private static final Logger log = LoggerFactory.getLogger(Profiler.class);
+
+ public static void printSystemLoad(Logger log) {
+ Runtime runtime = Runtime.getRuntime();
+ long free = runtime.freeMemory() / 1024 / 1024;
+ long total = runtime.totalMemory() / 1024 / 1024;
+ long used = total - free;
+ log.info("System load (nr. threads/used memory (MB)): " + Thread.activeCount() + "/" + used);
+ }
+
+}
diff --git a/common/src/main/java/io/bitsquare/common/util/Tuple2.java b/common/src/main/java/io/bitsquare/common/util/Tuple2.java
new file mode 100644
index 0000000000..8d2530f9e9
--- /dev/null
+++ b/common/src/main/java/io/bitsquare/common/util/Tuple2.java
@@ -0,0 +1,47 @@
+/*
+ * 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;
+
+public class Tuple2 {
+ final public A first;
+ final public B second;
+
+ public Tuple2(A first, B second) {
+ this.first = first;
+ this.second = second;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Tuple2)) return false;
+
+ Tuple2, ?> tuple2 = (Tuple2, ?>) o;
+
+ if (first != null ? !first.equals(tuple2.first) : tuple2.first != null) return false;
+ return !(second != null ? !second.equals(tuple2.second) : tuple2.second != null);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = first != null ? first.hashCode() : 0;
+ result = 31 * result + (second != null ? second.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/common/src/main/java/io/bitsquare/common/util/Tuple3.java b/common/src/main/java/io/bitsquare/common/util/Tuple3.java
new file mode 100644
index 0000000000..3229f98fd7
--- /dev/null
+++ b/common/src/main/java/io/bitsquare/common/util/Tuple3.java
@@ -0,0 +1,51 @@
+/*
+ * 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;
+
+public class Tuple3 {
+ final public A first;
+ final public B second;
+ final public C third;
+
+ public Tuple3(A first, B second, C third) {
+ this.first = first;
+ this.second = second;
+ this.third = third;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Tuple3)) return false;
+
+ Tuple3, ?, ?> tuple3 = (Tuple3, ?, ?>) o;
+
+ if (first != null ? !first.equals(tuple3.first) : tuple3.first != null) return false;
+ if (second != null ? !second.equals(tuple3.second) : tuple3.second != null) return false;
+ return !(third != null ? !third.equals(tuple3.third) : tuple3.third != null);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = first != null ? first.hashCode() : 0;
+ result = 31 * result + (second != null ? second.hashCode() : 0);
+ result = 31 * result + (third != null ? third.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/alert/Alert.java b/core/src/main/java/io/bitsquare/alert/Alert.java
new file mode 100644
index 0000000000..1a0a3751d2
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/alert/Alert.java
@@ -0,0 +1,84 @@
+/*
+ * 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 io.bitsquare.app.Version;
+import io.bitsquare.p2p.storage.data.PubKeyProtectedExpirablePayload;
+
+import java.security.PublicKey;
+
+public final class Alert implements PubKeyProtectedExpirablePayload {
+ // That object is sent over the wire, so we need to take care of version compatibility.
+ private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
+
+ public static final long TTL = 10 * 24 * 60 * 60 * 1000; // 10 days
+
+ public final String message;
+ private String signatureAsBase64;
+ private PublicKey storagePublicKey;
+
+ public Alert(String message) {
+ this.message = message;
+ }
+
+ public void setSigAndStoragePubKey(String signatureAsBase64, PublicKey storagePublicKey) {
+ this.signatureAsBase64 = signatureAsBase64;
+ this.storagePublicKey = storagePublicKey;
+ }
+
+ public String getSignatureAsBase64() {
+ return signatureAsBase64;
+ }
+
+ @Override
+ public long getTTL() {
+ return TTL;
+ }
+
+ @Override
+ public PublicKey getPubKey() {
+ return storagePublicKey;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Alert)) return false;
+
+ Alert that = (Alert) o;
+
+ if (message != null ? !message.equals(that.message) : that.message != null) return false;
+ return !(getSignatureAsBase64() != null ? !getSignatureAsBase64().equals(that.getSignatureAsBase64()) : that.getSignatureAsBase64() != null);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = message != null ? message.hashCode() : 0;
+ result = 31 * result + (getSignatureAsBase64() != null ? getSignatureAsBase64().hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "AlertMessage{" +
+ "message='" + message + '\'' +
+ ", signature.hashCode()='" + signatureAsBase64.hashCode() + '\'' +
+ '}';
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/alert/AlertManager.java b/core/src/main/java/io/bitsquare/alert/AlertManager.java
new file mode 100644
index 0000000000..fec210fd0c
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/alert/AlertManager.java
@@ -0,0 +1,144 @@
+/*
+ * 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 io.bitsquare.common.crypto.KeyRing;
+import io.bitsquare.p2p.storage.HashSetChangedListener;
+import io.bitsquare.p2p.storage.data.ProtectedData;
+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.io.Serializable;
+import java.math.BigInteger;
+import java.security.SignatureException;
+
+import static org.bitcoinj.core.Utils.HEX;
+
+public class AlertManager {
+ transient private static final Logger log = LoggerFactory.getLogger(AlertManager.class);
+
+ private final AlertService alertService;
+ private KeyRing keyRing;
+ private User user;
+ private final ObjectProperty alertMessageProperty = new SimpleObjectProperty<>();
+
+ // Pub key for developer global alert message
+ private static final String devPubKeyAsHex = "02682880ae61fc1ea9375198bf2b5594fc3ed28074d3f5f0ed907e38acc5fb1fdc";
+ private ECKey alertSigningKey;
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Constructor, Initialization
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ @Inject
+ public AlertManager(AlertService alertService, KeyRing keyRing, User user) {
+ this.alertService = alertService;
+ this.keyRing = keyRing;
+ this.user = user;
+
+ alertService.addHashSetChangedListener(new HashSetChangedListener() {
+ @Override
+ public void onAdded(ProtectedData entry) {
+ Serializable data = entry.expirablePayload;
+ if (data instanceof Alert) {
+ Alert alert = (Alert) data;
+ if (verifySignature(alert))
+ alertMessageProperty.set(alert);
+ }
+ }
+
+ @Override
+ public void onRemoved(ProtectedData entry) {
+ Serializable data = entry.expirablePayload;
+ if (data instanceof Alert) {
+ Alert alert = (Alert) data;
+ 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);
+ alertService.addAlertMessage(alert, null, null);
+ }
+ return isKeyValid;
+ }
+
+ public boolean removeAlertMessageIfKeyIsValid(String privKeyString) {
+ Alert developersAlert = user.getDevelopersAlert();
+ if (isKeyValid(privKeyString) && developersAlert != null) {
+ alertService.removeAlertMessage(developersAlert, null, null);
+ user.setDevelopersAlert(null);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private boolean isKeyValid(String privKeyString) {
+ try {
+ alertSigningKey = ECKey.fromPrivate(new BigInteger(1, HEX.decode(privKeyString)));
+ return devPubKeyAsHex.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.setSigAndStoragePubKey(signatureAsBase64, keyRing.getStorageSignatureKeyPair().getPublic());
+ }
+
+ private boolean verifySignature(Alert alert) {
+ String alertMessageAsHex = Utils.HEX.encode(alert.message.getBytes());
+ try {
+ ECKey.fromPublicOnly(HEX.decode(devPubKeyAsHex)).verifyMessage(alertMessageAsHex, alert.getSignatureAsBase64());
+ return true;
+ } catch (SignatureException e) {
+ log.warn("verifySignature failed");
+ return false;
+ }
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/alert/AlertModule.java b/core/src/main/java/io/bitsquare/alert/AlertModule.java
new file mode 100644
index 0000000000..15224b2e48
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/alert/AlertModule.java
@@ -0,0 +1,38 @@
+/*
+ * 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.Singleton;
+import io.bitsquare.app.AppModule;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.env.Environment;
+
+public class AlertModule extends AppModule {
+ private static final Logger log = LoggerFactory.getLogger(AlertModule.class);
+
+ public AlertModule(Environment env) {
+ super(env);
+ }
+
+ @Override
+ protected final void configure() {
+ bind(AlertManager.class).in(Singleton.class);
+ bind(AlertService.class).in(Singleton.class);
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/alert/AlertService.java b/core/src/main/java/io/bitsquare/alert/AlertService.java
new file mode 100644
index 0000000000..da1f8b43c2
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/alert/AlertService.java
@@ -0,0 +1,71 @@
+/*
+ * 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 io.bitsquare.common.handlers.ErrorMessageHandler;
+import io.bitsquare.common.handlers.ResultHandler;
+import io.bitsquare.p2p.P2PService;
+import io.bitsquare.p2p.storage.HashSetChangedListener;
+import org.jetbrains.annotations.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
+
+/**
+ * Used to load global alert messages.
+ * The message is signed by the project developers private key and use data protection.
+ */
+public class AlertService {
+ private static final Logger log = LoggerFactory.getLogger(AlertService.class);
+ private P2PService p2PService;
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Constructor, Initialization
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ @Inject
+ public AlertService(P2PService p2PService) {
+ this.p2PService = p2PService;
+ }
+
+ public void addHashSetChangedListener(HashSetChangedListener hashSetChangedListener) {
+ p2PService.addHashSetChangedListener(hashSetChangedListener);
+ }
+
+ public void addAlertMessage(Alert alert, @Nullable ResultHandler resultHandler, @Nullable ErrorMessageHandler errorMessageHandler) {
+ boolean result = p2PService.addData(alert);
+ if (result) {
+ log.trace("Add alertMessage to network was successful. AlertMessage = " + alert);
+ if (resultHandler != null) resultHandler.handleResult();
+ } else {
+ if (errorMessageHandler != null) errorMessageHandler.handleErrorMessage("Add alertMessage failed");
+ }
+ }
+
+ public void removeAlertMessage(Alert alert, @Nullable ResultHandler resultHandler, @Nullable ErrorMessageHandler errorMessageHandler) {
+ if (p2PService.removeData(alert)) {
+ log.trace("Remove alertMessage from network was successful. AlertMessage = " + alert);
+ if (resultHandler != null) resultHandler.handleResult();
+ } else {
+ if (errorMessageHandler != null) errorMessageHandler.handleErrorMessage("Remove alertMessage failed");
+ }
+ }
+
+}
diff --git a/core/src/main/java/io/bitsquare/arbitration/ArbitratorManager.java b/core/src/main/java/io/bitsquare/arbitration/ArbitratorManager.java
new file mode 100644
index 0000000000..86a706f8c8
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/arbitration/ArbitratorManager.java
@@ -0,0 +1,264 @@
+/*
+ * 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 com.google.inject.name.Named;
+import io.bitsquare.app.ProgramArguments;
+import io.bitsquare.common.crypto.KeyRing;
+import io.bitsquare.common.handlers.ErrorMessageHandler;
+import io.bitsquare.common.handlers.ResultHandler;
+import io.bitsquare.p2p.Address;
+import io.bitsquare.p2p.P2PService;
+import io.bitsquare.p2p.P2PServiceListener;
+import io.bitsquare.p2p.storage.HashSetChangedListener;
+import io.bitsquare.p2p.storage.data.ProtectedData;
+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.reactfx.util.FxTimer;
+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.time.Duration;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import static org.bitcoinj.core.Utils.HEX;
+
+public class ArbitratorManager {
+ transient private static final Logger log = LoggerFactory.getLogger(ArbitratorManager.class);
+
+ private final KeyRing keyRing;
+ private final ArbitratorService arbitratorService;
+ private final User user;
+ private final ObservableMap arbitratorsObservableMap = FXCollections.observableHashMap();
+
+ // Keys for invited arbitrators in bootstrapping phase (before registration is open to anyone and security payment is implemented)
+ // For testing purpose here is a private key so anyone can setup an arbitrator for now.
+ // The matching pubkey will be removed once we use real arbitrators.
+ // PrivKey for testing: 6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a
+ // Matching pubKey: 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",
+ "0274f772a98d23e7a0251ab30d7121897b5aebd11a2f1e45ab654aa57503173245",
+ "036d8a1dfcb406886037d2381da006358722823e1940acc2598c844bbc0fd1026f"
+ ));
+ private static final String publicKeyForTesting = "027a381b5333a56e1cc3d90d3a7d07f26509adf7029ed06fc997c656621f8da1ee";
+ private final boolean isDevTest;
+ private P2PServiceListener p2PServiceListener;
+
+ @Inject
+ public ArbitratorManager(@Named(ProgramArguments.DEV_TEST) boolean isDevTest, KeyRing keyRing, ArbitratorService arbitratorService, User user) {
+ this.isDevTest = isDevTest;
+ this.keyRing = keyRing;
+ this.arbitratorService = arbitratorService;
+ this.user = user;
+
+ arbitratorService.addHashSetChangedListener(new HashSetChangedListener() {
+ @Override
+ public void onAdded(ProtectedData entry) {
+ applyArbitrators();
+ }
+
+ @Override
+ public void onRemoved(ProtectedData entry) {
+ applyArbitrators();
+ }
+ });
+ }
+
+ public void onAllServicesInitialized() {
+ if (user.getRegisteredArbitrator() != null) {
+
+ P2PService p2PService = arbitratorService.getP2PService();
+ if (!p2PService.isAuthenticated()) {
+ p2PServiceListener = new P2PServiceListener() {
+ @Override
+ public void onTorNodeReady() {
+ }
+
+ @Override
+ public void onHiddenServiceReady() {
+ }
+
+ @Override
+ public void onSetupFailed(Throwable throwable) {
+ }
+
+ @Override
+ public void onAllDataReceived() {
+ }
+
+ @Override
+ public void onAuthenticated() {
+ republishArbitrator();
+ }
+ };
+ p2PService.addP2PServiceListener(p2PServiceListener);
+
+ } else {
+ republishArbitrator();
+ }
+
+ // re-publish periodically
+ FxTimer.runPeriodically(
+ Duration.ofMillis(Arbitrator.TTL / 2),
+ () -> republishArbitrator()
+ );
+ }
+
+ applyArbitrators();
+ }
+
+ private void republishArbitrator() {
+ if (p2PServiceListener != null) arbitratorService.getP2PService().removeP2PServiceListener(p2PServiceListener);
+
+ Arbitrator registeredArbitrator = user.getRegisteredArbitrator();
+ if (registeredArbitrator != null) {
+ addArbitrator(registeredArbitrator,
+ this::applyArbitrators,
+ log::error
+ );
+ }
+ }
+
+ public void applyArbitrators() {
+ Map map = arbitratorService.getArbitrators();
+ log.trace("Arbitrators . size=" + (map.values() != null ? map.values().size() : "0"));
+ arbitratorsObservableMap.clear();
+ Map filtered = map.values().stream()
+ .filter(e -> isPublicKeyInList(Utils.HEX.encode(e.getRegistrationPubKey()))
+ && verifySignature(e.getPubKeyRing().getStorageSignaturePubKey(), e.getRegistrationPubKey(), e.getRegistrationSignature()))
+ .collect(Collectors.toMap(Arbitrator::getArbitratorAddress, Function.identity()));
+
+ arbitratorsObservableMap.putAll(filtered);
+
+ log.debug("filtered arbitrators: " + arbitratorsObservableMap.values());
+ log.trace("user.getAcceptedArbitrators(): " + user.getAcceptedArbitrators().toString());
+
+ // we need to remove accepted arbitrators which are not available anymore
+ if (user.getAcceptedArbitrators() != null) {
+ List removeList = user.getAcceptedArbitrators().stream()
+ .filter(e -> !arbitratorsObservableMap.containsValue(e))
+ .collect(Collectors.toList());
+ removeList.stream().forEach(user::removeAcceptedArbitrator);
+ log.trace("removeList arbitrators: " + removeList.toString());
+ log.trace("user.getAcceptedArbitrators(): " + user.getAcceptedArbitrators().toString());
+
+ // if we don't have any arbitrator anymore we set all matching
+ if (user.getAcceptedArbitrators().isEmpty()) {
+ arbitratorsObservableMap.values().stream()
+ .filter(arbitrator -> user.hasMatchingLanguage(arbitrator))
+ .forEach(arbitrator -> user.addAcceptedArbitrator(arbitrator));
+ }
+
+ log.trace("user.getAcceptedArbitrators(): " + user.getAcceptedArbitrators().toString());
+ }
+ }
+
+ public void addArbitrator(Arbitrator arbitrator, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
+ user.setRegisteredArbitrator(arbitrator);
+ arbitratorsObservableMap.put(arbitrator.getArbitratorAddress(), arbitrator);
+ arbitratorService.addArbitrator(arbitrator,
+ () -> {
+ log.debug("Arbitrator successfully saved in P2P network");
+ resultHandler.handleResult();
+
+ if (arbitratorsObservableMap.size() > 0)
+ FxTimer.runLater(Duration.ofMillis(1000), this::applyArbitrators);
+ },
+ errorMessageHandler::handleErrorMessage);
+ }
+
+ public void removeArbitrator(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
+ Arbitrator registeredArbitrator = user.getRegisteredArbitrator();
+ if (registeredArbitrator != null) {
+ user.setRegisteredArbitrator(null);
+ arbitratorsObservableMap.remove(registeredArbitrator.getArbitratorAddress());
+ 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().getStorageSignaturePubKey().getEncoded());
+ return key.signMessage(keyToSignAsHex);
+ }
+
+ 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;
+ }
+ }
+
+ @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 isDevTest && pubKeyAsHex.equals(publicKeyForTesting) || publicKeys.contains(pubKeyAsHex);
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/arbitration/Dispute.java b/core/src/main/java/io/bitsquare/arbitration/Dispute.java
new file mode 100644
index 0000000000..aa1f1ecaf1
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/arbitration/Dispute.java
@@ -0,0 +1,354 @@
+/*
+ * 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 io.bitsquare.app.Version;
+import io.bitsquare.arbitration.messages.DisputeMailMessage;
+import io.bitsquare.common.crypto.PubKeyRing;
+import io.bitsquare.storage.Storage;
+import io.bitsquare.trade.Contract;
+import javafx.beans.property.*;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nullable;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+public class Dispute implements Serializable {
+ // That object is sent over the wire, so we need to take care of version compatibility.
+ private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
+ transient private static final Logger log = LoggerFactory.getLogger(Dispute.class);
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Fields
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ private final String tradeId;
+ private final int traderId;
+ private final boolean disputeOpenerIsBuyer;
+ private final boolean disputeOpenerIsOfferer;
+ private final long openingDate;
+ private final PubKeyRing traderPubKeyRing;
+ private final long tradeDate;
+ private final Contract contract;
+ private final byte[] contractHash;
+ @Nullable
+ private final byte[] depositTxSerialized;
+ @Nullable
+ private final byte[] payoutTxSerialized;
+ @Nullable
+ private final String depositTxId;
+ @Nullable
+ private final String payoutTxId;
+ private final String contractAsJson;
+ private final String offererContractSignature;
+ private final String takerContractSignature;
+ private final PubKeyRing arbitratorPubKeyRing;
+ private final boolean isSupportTicket;
+
+ private final List disputeMailMessages = new ArrayList<>();
+
+ private boolean isClosed;
+ private DisputeResult disputeResult;
+
+ transient private Storage> storage;
+ transient private ObservableList disputeMailMessagesAsObservableList = FXCollections.observableArrayList(disputeMailMessages);
+ transient private BooleanProperty isClosedProperty = new SimpleBooleanProperty(isClosed);
+ transient private ObjectProperty disputeResultProperty = new SimpleObjectProperty<>(disputeResult);
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Constructor
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ public Dispute(Storage> storage,
+ String tradeId,
+ int traderId,
+ boolean disputeOpenerIsBuyer,
+ boolean disputeOpenerIsOfferer,
+ PubKeyRing traderPubKeyRing,
+ Date tradeDate,
+ Contract contract,
+ byte[] contractHash,
+ @Nullable byte[] depositTxSerialized,
+ @Nullable byte[] payoutTxSerialized,
+ @Nullable String depositTxId,
+ @Nullable String payoutTxId,
+ String contractAsJson,
+ String offererContractSignature,
+ String takerContractSignature,
+ PubKeyRing arbitratorPubKeyRing,
+ boolean isSupportTicket) {
+ this.storage = storage;
+ this.tradeId = tradeId;
+ this.traderId = traderId;
+ this.disputeOpenerIsBuyer = disputeOpenerIsBuyer;
+ this.disputeOpenerIsOfferer = disputeOpenerIsOfferer;
+ this.traderPubKeyRing = traderPubKeyRing;
+ this.tradeDate = tradeDate.getTime();
+ this.contract = contract;
+ this.contractHash = contractHash;
+ this.depositTxSerialized = depositTxSerialized;
+ this.payoutTxSerialized = payoutTxSerialized;
+ this.depositTxId = depositTxId;
+ this.payoutTxId = payoutTxId;
+ this.contractAsJson = contractAsJson;
+ this.offererContractSignature = offererContractSignature;
+ this.takerContractSignature = takerContractSignature;
+ this.arbitratorPubKeyRing = arbitratorPubKeyRing;
+ this.isSupportTicket = isSupportTicket;
+ this.openingDate = new Date().getTime();
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ try {
+ in.defaultReadObject();
+ disputeMailMessagesAsObservableList = FXCollections.observableArrayList(disputeMailMessages);
+ disputeResultProperty = new SimpleObjectProperty<>(disputeResult);
+ isClosedProperty = new SimpleBooleanProperty(isClosed);
+ } catch (Throwable t) {
+ log.trace("Cannot be deserialized." + t.getMessage());
+ }
+ }
+
+ public void addDisputeMessage(DisputeMailMessage disputeMailMessage) {
+ if (!disputeMailMessages.contains(disputeMailMessage)) {
+ disputeMailMessages.add(disputeMailMessage);
+ disputeMailMessagesAsObservableList.add(disputeMailMessage);
+ storage.queueUpForSave();
+ } else {
+ log.error("disputeMailMessage already exists");
+ }
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Setters
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ // In case we get the object via the network storage is not set as its transient, so we need to set it.
+ public void setStorage(Storage> storage) {
+ this.storage = storage;
+ }
+
+ public void setIsClosed(boolean isClosed) {
+ this.isClosed = isClosed;
+ isClosedProperty.set(isClosed);
+ storage.queueUpForSave();
+ }
+
+ public void setDisputeResult(DisputeResult disputeResult) {
+ this.disputeResult = disputeResult;
+ disputeResultProperty.set(disputeResult);
+ storage.queueUpForSave();
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Getters
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ public String getTradeId() {
+ return tradeId;
+ }
+
+ public String getShortTradeId() {
+ return tradeId.substring(0, 8);
+ }
+
+ public int getTraderId() {
+ return traderId;
+ }
+
+ public boolean isDisputeOpenerIsBuyer() {
+ return disputeOpenerIsBuyer;
+ }
+
+ public boolean isDisputeOpenerIsOfferer() {
+ return disputeOpenerIsOfferer;
+ }
+
+ public Date getOpeningDate() {
+ return new Date(openingDate);
+ }
+
+ public PubKeyRing getTraderPubKeyRing() {
+ return traderPubKeyRing;
+ }
+
+ public Contract getContract() {
+ return contract;
+ }
+
+ @Nullable
+ public byte[] getDepositTxSerialized() {
+ return depositTxSerialized;
+ }
+
+ @Nullable
+ public byte[] getPayoutTxSerialized() {
+ return payoutTxSerialized;
+ }
+
+ @Nullable
+ public String getDepositTxId() {
+ return depositTxId;
+ }
+
+ @Nullable
+ public String getPayoutTxId() {
+ return payoutTxId;
+ }
+
+ public String getContractAsJson() {
+ return contractAsJson;
+ }
+
+ public String getOffererContractSignature() {
+ return offererContractSignature;
+ }
+
+ public String getTakerContractSignature() {
+ return takerContractSignature;
+ }
+
+ public ObservableList getDisputeMailMessagesAsObservableList() {
+ return disputeMailMessagesAsObservableList;
+ }
+
+ public boolean isClosed() {
+ return isClosedProperty.get();
+ }
+
+ public ReadOnlyBooleanProperty isClosedProperty() {
+ return isClosedProperty;
+ }
+
+ public PubKeyRing getArbitratorPubKeyRing() {
+ return arbitratorPubKeyRing;
+ }
+
+ public ObjectProperty disputeResultProperty() {
+ return disputeResultProperty;
+ }
+
+ public boolean isSupportTicket() {
+ return isSupportTicket;
+ }
+
+ public byte[] getContractHash() {
+ return contractHash;
+ }
+
+ public Date getTradeDate() {
+ return new Date(tradeDate);
+ }
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Dispute)) return false;
+
+ Dispute dispute = (Dispute) o;
+
+ if (traderId != dispute.traderId) return false;
+ if (disputeOpenerIsBuyer != dispute.disputeOpenerIsBuyer) return false;
+ if (disputeOpenerIsOfferer != dispute.disputeOpenerIsOfferer) return false;
+ if (openingDate != dispute.openingDate) return false;
+ if (tradeDate != dispute.tradeDate) return false;
+ if (isSupportTicket != dispute.isSupportTicket) return false;
+ if (isClosed != dispute.isClosed) return false;
+ if (tradeId != null ? !tradeId.equals(dispute.tradeId) : dispute.tradeId != null) return false;
+ if (traderPubKeyRing != null ? !traderPubKeyRing.equals(dispute.traderPubKeyRing) : dispute.traderPubKeyRing != null)
+ return false;
+ if (contract != null ? !contract.equals(dispute.contract) : dispute.contract != null) return false;
+ if (!Arrays.equals(contractHash, dispute.contractHash)) return false;
+ if (!Arrays.equals(depositTxSerialized, dispute.depositTxSerialized)) return false;
+ if (!Arrays.equals(payoutTxSerialized, dispute.payoutTxSerialized)) return false;
+ if (depositTxId != null ? !depositTxId.equals(dispute.depositTxId) : dispute.depositTxId != null) return false;
+ if (payoutTxId != null ? !payoutTxId.equals(dispute.payoutTxId) : dispute.payoutTxId != null) return false;
+ if (contractAsJson != null ? !contractAsJson.equals(dispute.contractAsJson) : dispute.contractAsJson != null)
+ return false;
+ if (offererContractSignature != null ? !offererContractSignature.equals(dispute.offererContractSignature) : dispute.offererContractSignature != null)
+ return false;
+ if (takerContractSignature != null ? !takerContractSignature.equals(dispute.takerContractSignature) : dispute.takerContractSignature != null)
+ return false;
+ if (arbitratorPubKeyRing != null ? !arbitratorPubKeyRing.equals(dispute.arbitratorPubKeyRing) : dispute.arbitratorPubKeyRing != null)
+ return false;
+ if (disputeMailMessages != null ? !disputeMailMessages.equals(dispute.disputeMailMessages) : dispute.disputeMailMessages != null)
+ return false;
+ return !(disputeResult != null ? !disputeResult.equals(dispute.disputeResult) : dispute.disputeResult != null);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = tradeId != null ? tradeId.hashCode() : 0;
+ result = 31 * result + traderId;
+ result = 31 * result + (disputeOpenerIsBuyer ? 1 : 0);
+ result = 31 * result + (disputeOpenerIsOfferer ? 1 : 0);
+ result = 31 * result + (int) (openingDate ^ (openingDate >>> 32));
+ result = 31 * result + (traderPubKeyRing != null ? traderPubKeyRing.hashCode() : 0);
+ result = 31 * result + (int) (tradeDate ^ (tradeDate >>> 32));
+ result = 31 * result + (contract != null ? contract.hashCode() : 0);
+ result = 31 * result + (contractHash != null ? Arrays.hashCode(contractHash) : 0);
+ result = 31 * result + (depositTxSerialized != null ? Arrays.hashCode(depositTxSerialized) : 0);
+ result = 31 * result + (payoutTxSerialized != null ? Arrays.hashCode(payoutTxSerialized) : 0);
+ result = 31 * result + (depositTxId != null ? depositTxId.hashCode() : 0);
+ result = 31 * result + (payoutTxId != null ? payoutTxId.hashCode() : 0);
+ result = 31 * result + (contractAsJson != null ? contractAsJson.hashCode() : 0);
+ result = 31 * result + (offererContractSignature != null ? offererContractSignature.hashCode() : 0);
+ result = 31 * result + (takerContractSignature != null ? takerContractSignature.hashCode() : 0);
+ result = 31 * result + (arbitratorPubKeyRing != null ? arbitratorPubKeyRing.hashCode() : 0);
+ result = 31 * result + (isSupportTicket ? 1 : 0);
+ result = 31 * result + (disputeMailMessages != null ? disputeMailMessages.hashCode() : 0);
+ result = 31 * result + (isClosed ? 1 : 0);
+ result = 31 * result + (disputeResult != null ? disputeResult.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "Dispute{" +
+ ", tradeId='" + tradeId + '\'' +
+ ", traderId='" + traderId + '\'' +
+ ", disputeOpenerIsBuyer=" + disputeOpenerIsBuyer +
+ ", disputeOpenerIsOfferer=" + disputeOpenerIsOfferer +
+ ", openingDate=" + openingDate +
+ ", traderPubKeyRing=" + traderPubKeyRing +
+ ", contract=" + contract +
+ ", contractAsJson='" + contractAsJson + '\'' +
+ ", buyerContractSignature='" + offererContractSignature + '\'' +
+ ", sellerContractSignature='" + takerContractSignature + '\'' +
+ ", arbitratorPubKeyRing=" + arbitratorPubKeyRing +
+ ", disputeMailMessages=" + disputeMailMessages +
+ ", disputeMailMessagesAsObservableList=" + disputeMailMessagesAsObservableList +
+ ", isClosed=" + isClosed +
+ ", disputeResult=" + disputeResult +
+ ", disputeResultProperty=" + disputeResultProperty +
+ '}';
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/arbitration/DisputeList.java b/core/src/main/java/io/bitsquare/arbitration/DisputeList.java
new file mode 100644
index 0000000000..0855a80c25
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/arbitration/DisputeList.java
@@ -0,0 +1,93 @@
+/*
+ * 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 io.bitsquare.app.Version;
+import io.bitsquare.storage.Storage;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import org.jetbrains.annotations.NotNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+
+public class DisputeList extends ArrayList implements Serializable {
+ // That object is saved to disc. We need to take care of changes to not break deserialization.
+ private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
+
+ private static final Logger log = LoggerFactory.getLogger(DisputeList.class);
+
+ final transient private Storage> storage;
+ transient private ObservableList observableList;
+
+ public DisputeList(Storage> storage) {
+ this.storage = storage;
+
+ DisputeList persisted = storage.initAndGetPersisted(this);
+ if (persisted != null) {
+ this.addAll(persisted);
+ }
+ observableList = FXCollections.observableArrayList(this);
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ try {
+ in.defaultReadObject();
+ } catch (Throwable t) {
+ log.trace("Cannot be deserialized." + t.getMessage());
+ }
+ }
+
+ @Override
+ public boolean add(DisputeCase disputeCase) {
+ if (!super.contains(disputeCase)) {
+ boolean result = super.add(disputeCase);
+ getObservableList().add(disputeCase);
+ storage.queueUpForSave();
+ return result;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean remove(Object disputeCase) {
+ boolean result = super.remove(disputeCase);
+ getObservableList().remove(disputeCase);
+ storage.queueUpForSave();
+ return result;
+ }
+
+ private ObservableList getObservableList() {
+ if (observableList == null)
+ observableList = FXCollections.observableArrayList(this);
+ return observableList;
+ }
+
+ @NotNull
+ @Override
+ public String toString() {
+ return "DisputeList{" +
+ ", observableList=" + observableList +
+ '}';
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/arbitration/DisputeManager.java b/core/src/main/java/io/bitsquare/arbitration/DisputeManager.java
new file mode 100644
index 0000000000..6ff800ca0e
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/arbitration/DisputeManager.java
@@ -0,0 +1,608 @@
+/*
+ * 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.common.util.concurrent.FutureCallback;
+import com.google.inject.Inject;
+import io.bitsquare.arbitration.messages.*;
+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.crypto.KeyRing;
+import io.bitsquare.common.crypto.PubKeyRing;
+import io.bitsquare.p2p.Address;
+import io.bitsquare.p2p.Message;
+import io.bitsquare.p2p.P2PService;
+import io.bitsquare.p2p.P2PServiceListener;
+import io.bitsquare.p2p.messaging.DecryptedMessageWithPubKey;
+import io.bitsquare.p2p.messaging.SendMailboxMessageListener;
+import io.bitsquare.storage.Storage;
+import io.bitsquare.trade.Contract;
+import io.bitsquare.trade.Trade;
+import io.bitsquare.trade.TradeManager;
+import io.bitsquare.trade.offer.OpenOffer;
+import io.bitsquare.trade.offer.OpenOfferManager;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import org.bitcoinj.core.AddressFormatException;
+import org.bitcoinj.core.Transaction;
+import org.jetbrains.annotations.NotNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Named;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.stream.Collectors;
+
+public class DisputeManager {
+ private static final Logger log = LoggerFactory.getLogger(DisputeManager.class);
+
+ private final TradeWalletService tradeWalletService;
+ private final WalletService walletService;
+ private final TradeManager tradeManager;
+ private final OpenOfferManager openOfferManager;
+ private final P2PService p2PService;
+ private final KeyRing keyRing;
+ private final Storage> disputeStorage;
+ private final DisputeList disputes;
+ transient private final ObservableList disputesObservableList;
+ private final String disputeInfo;
+ private final P2PServiceListener p2PServiceListener;
+ private final List decryptedMailboxMessageWithPubKeys = new CopyOnWriteArrayList<>();
+ private final List decryptedMailMessageWithPubKeys = new CopyOnWriteArrayList<>();
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Constructor
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ @Inject
+ public DisputeManager(P2PService p2PService,
+ TradeWalletService tradeWalletService,
+ WalletService walletService,
+ TradeManager tradeManager,
+ OpenOfferManager openOfferManager,
+ KeyRing keyRing,
+ @Named("storage.dir") File storageDir) {
+ this.p2PService = p2PService;
+ this.tradeWalletService = tradeWalletService;
+ this.walletService = walletService;
+ this.tradeManager = tradeManager;
+ this.openOfferManager = openOfferManager;
+ this.keyRing = keyRing;
+
+ disputeStorage = new Storage<>(storageDir);
+ disputes = new DisputeList<>(disputeStorage);
+ disputesObservableList = FXCollections.observableArrayList(disputes);
+ disputes.stream().forEach(e -> e.setStorage(getDisputeStorage()));
+
+ disputeInfo = "Please note the basic rules for the dispute process:\n" +
+ "1. You need to respond to the arbitrators requests in between 2 days.\n" +
+ "2. The maximum period for the dispute is 14 days.\n" +
+ "3. You need to fulfill what the arbitrator is requesting from you to deliver evidence for your case.\n" +
+ "4. You accepted the rules outlined in the wiki in the user agreement when you first started the application.\n\n" +
+ "Please read more in detail about the dispute process in our wiki:\nhttps://github" +
+ ".com/bitsquare/bitsquare/wiki/Dispute-process";
+
+ p2PService.addDecryptedMailListener((decryptedMessageWithPubKey, senderAddress) -> {
+ decryptedMailMessageWithPubKeys.add(decryptedMessageWithPubKey);
+ if (p2PService.isAuthenticated())
+ applyMessages();
+ });
+ p2PService.addDecryptedMailboxListener((decryptedMessageWithPubKey, senderAddress) -> {
+ decryptedMailboxMessageWithPubKeys.add(decryptedMessageWithPubKey);
+ if (p2PService.isAuthenticated())
+ applyMessages();
+ });
+
+ p2PServiceListener = new P2PServiceListener() {
+ @Override
+ public void onTorNodeReady() {
+ }
+
+ @Override
+ public void onHiddenServiceReady() {
+ }
+
+ @Override
+ public void onSetupFailed(Throwable throwable) {
+ }
+
+ @Override
+ public void onAllDataReceived() {
+ }
+
+ @Override
+ public void onAuthenticated() {
+ applyMessages();
+ }
+ };
+ p2PService.addP2PServiceListener(p2PServiceListener);
+ }
+
+ private void applyMessages() {
+ decryptedMailMessageWithPubKeys.forEach(decryptedMessageWithPubKey -> {
+ Message message = decryptedMessageWithPubKey.message;
+ if (message instanceof DisputeMessage)
+ dispatchMessage((DisputeMessage) message);
+ });
+ decryptedMailMessageWithPubKeys.clear();
+
+ decryptedMailboxMessageWithPubKeys.forEach(decryptedMessageWithPubKey -> {
+ Message message = decryptedMessageWithPubKey.message;
+ log.debug("decryptedMessageWithPubKey.message " + message);
+ if (message instanceof DisputeMessage) {
+ dispatchMessage((DisputeMessage) message);
+ p2PService.removeEntryFromMailbox(decryptedMessageWithPubKey);
+ }
+ });
+ decryptedMailboxMessageWithPubKeys.clear();
+
+ p2PService.removeP2PServiceListener(p2PServiceListener);
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // API
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ public void onAllServicesInitialized() {
+ }
+
+ private void dispatchMessage(DisputeMessage message) {
+ if (message instanceof OpenNewDisputeMessage)
+ onOpenNewDisputeMessage((OpenNewDisputeMessage) message);
+ else if (message instanceof PeerOpenedDisputeMessage)
+ onPeerOpenedDisputeMessage((PeerOpenedDisputeMessage) message);
+ else if (message instanceof DisputeMailMessage)
+ onDisputeMailMessage((DisputeMailMessage) message);
+ else if (message instanceof DisputeResultMessage)
+ onDisputeResultMessage((DisputeResultMessage) message);
+ else if (message instanceof PeerPublishedPayoutTxMessage)
+ onDisputedPayoutTxMessage((PeerPublishedPayoutTxMessage) message);
+ }
+
+ public void sendOpenNewDisputeMessage(Dispute dispute) {
+ if (!disputes.contains(dispute)) {
+ DisputeMailMessage disputeMailMessage = new DisputeMailMessage(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());
+ disputeMailMessage.setIsSystemMessage(true);
+ dispute.addDisputeMessage(disputeMailMessage);
+ disputes.add(dispute);
+ disputesObservableList.add(dispute);
+
+ p2PService.sendEncryptedMailboxMessage(dispute.getContract().arbitratorAddress,
+ dispute.getArbitratorPubKeyRing(),
+ new OpenNewDisputeMessage(dispute, p2PService.getAddress()),
+ new SendMailboxMessageListener() {
+ @Override
+ public void onArrived() {
+ disputeMailMessage.setArrived(true);
+ }
+
+ @Override
+ public void onStoredInMailbox() {
+ disputeMailMessage.setStoredInMailbox(true);
+ }
+
+ @Override
+ public void onFault() {
+ log.error("sendEncryptedMessage failed");
+ }
+ }
+ );
+
+ } else {
+ log.warn("We got a dispute msg what we have already stored. TradeId = " + dispute.getTradeId());
+ }
+ }
+
+ // arbitrator sends that to trading peer when he received openDispute request
+ private void sendPeerOpenedDisputeMessage(Dispute disputeFromOpener) {
+ Contract contractFromOpener = disputeFromOpener.getContract();
+ PubKeyRing pubKeyRing = disputeFromOpener.isDisputeOpenerIsBuyer() ? contractFromOpener.getSellerPubKeyRing() : contractFromOpener.getBuyerPubKeyRing();
+ Dispute dispute = new Dispute(
+ disputeStorage,
+ disputeFromOpener.getTradeId(),
+ pubKeyRing.hashCode(),
+ !disputeFromOpener.isDisputeOpenerIsBuyer(),
+ !disputeFromOpener.isDisputeOpenerIsOfferer(),
+ pubKeyRing,
+ disputeFromOpener.getTradeDate(),
+ contractFromOpener,
+ disputeFromOpener.getContractHash(),
+ disputeFromOpener.getDepositTxSerialized(),
+ disputeFromOpener.getPayoutTxSerialized(),
+ disputeFromOpener.getDepositTxId(),
+ disputeFromOpener.getPayoutTxId(),
+ disputeFromOpener.getContractAsJson(),
+ disputeFromOpener.getOffererContractSignature(),
+ disputeFromOpener.getTakerContractSignature(),
+ disputeFromOpener.getArbitratorPubKeyRing(),
+ disputeFromOpener.isSupportTicket()
+ );
+ DisputeMailMessage disputeMailMessage = new DisputeMailMessage(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());
+ disputeMailMessage.setIsSystemMessage(true);
+ dispute.addDisputeMessage(disputeMailMessage);
+ disputes.add(dispute);
+ disputesObservableList.add(dispute);
+
+ // we mirrored dispute already!
+ Contract contract = dispute.getContract();
+ PubKeyRing peersPubKeyRing = dispute.isDisputeOpenerIsBuyer() ? contract.getBuyerPubKeyRing() : contract.getSellerPubKeyRing();
+ Address peerAddress = dispute.isDisputeOpenerIsBuyer() ? contract.getBuyerAddress() : contract.getSellerAddress();
+ log.trace("sendPeerOpenedDisputeMessage to peerAddress " + peerAddress);
+ p2PService.sendEncryptedMailboxMessage(peerAddress,
+ peersPubKeyRing,
+ new PeerOpenedDisputeMessage(dispute, p2PService.getAddress()),
+ new SendMailboxMessageListener() {
+ @Override
+ public void onArrived() {
+ disputeMailMessage.setArrived(true);
+ }
+
+ @Override
+ public void onStoredInMailbox() {
+ disputeMailMessage.setStoredInMailbox(true);
+ }
+
+ @Override
+ public void onFault() {
+ log.error("sendEncryptedMessage failed");
+ }
+ }
+ );
+ }
+
+ // traders send msg to the arbitrator or arbitrator to 1 trader (trader to trader is not allowed)
+ public DisputeMailMessage sendDisputeMailMessage(Dispute dispute, String text, ArrayList attachments) {
+ DisputeMailMessage disputeMailMessage = new DisputeMailMessage(dispute.getTradeId(),
+ dispute.getTraderPubKeyRing().hashCode(),
+ isTrader(dispute),
+ text,
+ p2PService.getAddress());
+ disputeMailMessage.addAllAttachments(attachments);
+ PubKeyRing receiverPubKeyRing = null;
+ Address peerAddress = null;
+ if (isTrader(dispute)) {
+ dispute.addDisputeMessage(disputeMailMessage);
+ receiverPubKeyRing = dispute.getArbitratorPubKeyRing();
+ peerAddress = dispute.getContract().arbitratorAddress;
+ } else if (isArbitrator(dispute)) {
+ if (!disputeMailMessage.isSystemMessage())
+ dispute.addDisputeMessage(disputeMailMessage);
+ receiverPubKeyRing = dispute.getTraderPubKeyRing();
+ Contract contract = dispute.getContract();
+ if (contract.getBuyerPubKeyRing().equals(receiverPubKeyRing))
+ peerAddress = contract.getBuyerAddress();
+ else
+ peerAddress = contract.getSellerAddress();
+ } else {
+ log.error("That must not happen. Trader cannot communicate to other trader.");
+ }
+ if (receiverPubKeyRing != null) {
+ log.trace("sendDisputeMailMessage to peerAddress " + peerAddress);
+ p2PService.sendEncryptedMailboxMessage(peerAddress,
+ receiverPubKeyRing,
+ disputeMailMessage,
+ new SendMailboxMessageListener() {
+ @Override
+ public void onArrived() {
+ disputeMailMessage.setArrived(true);
+ }
+
+ @Override
+ public void onStoredInMailbox() {
+ disputeMailMessage.setStoredInMailbox(true);
+ }
+
+ @Override
+ public void onFault() {
+ log.error("sendEncryptedMessage failed");
+ }
+ }
+ );
+ }
+
+ return disputeMailMessage;
+ }
+
+ // arbitrator send result to trader
+ public void sendDisputeResultMessage(DisputeResult disputeResult, Dispute dispute, String text) {
+ DisputeMailMessage disputeMailMessage = new DisputeMailMessage(dispute.getTradeId(),
+ dispute.getTraderPubKeyRing().hashCode(),
+ false,
+ text,
+ p2PService.getAddress());
+ dispute.addDisputeMessage(disputeMailMessage);
+ disputeResult.setResultMailMessage(disputeMailMessage);
+
+ Address peerAddress;
+ Contract contract = dispute.getContract();
+ if (contract.getBuyerPubKeyRing().equals(dispute.getTraderPubKeyRing()))
+ peerAddress = contract.getBuyerAddress();
+ else
+ peerAddress = contract.getSellerAddress();
+ p2PService.sendEncryptedMailboxMessage(peerAddress,
+ dispute.getTraderPubKeyRing(),
+ new DisputeResultMessage(disputeResult, p2PService.getAddress()),
+ new SendMailboxMessageListener() {
+ @Override
+ public void onArrived() {
+ disputeMailMessage.setArrived(true);
+ }
+
+ @Override
+ public void onStoredInMailbox() {
+ disputeMailMessage.setStoredInMailbox(true);
+ }
+
+ @Override
+ public void onFault() {
+ log.error("sendEncryptedMessage failed");
+ }
+ }
+ );
+ }
+
+ // winner (or buyer in case of 50/50) sends tx to other peer
+ private void sendPeerPublishedPayoutTxMessage(Transaction transaction, Dispute dispute, Contract contract) {
+ PubKeyRing peersPubKeyRing = dispute.isDisputeOpenerIsBuyer() ? contract.getSellerPubKeyRing() : contract.getBuyerPubKeyRing();
+ Address peerAddress = dispute.isDisputeOpenerIsBuyer() ? contract.getSellerAddress() : contract.getBuyerAddress();
+ log.trace("sendPeerPublishedPayoutTxMessage to peerAddress " + peerAddress);
+ p2PService.sendEncryptedMailboxMessage(peerAddress,
+ peersPubKeyRing,
+ new PeerPublishedPayoutTxMessage(transaction.bitcoinSerialize(), dispute.getTradeId(), p2PService.getAddress()),
+ new SendMailboxMessageListener() {
+ @Override
+ public void onArrived() {
+
+ }
+
+ @Override
+ public void onStoredInMailbox() {
+
+ }
+
+ @Override
+ public void onFault() {
+ log.error("sendEncryptedMessage failed");
+ }
+ }
+ );
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Incoming message
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ // arbitrator receives that from trader who opens dispute
+ private void onOpenNewDisputeMessage(OpenNewDisputeMessage openNewDisputeMessage) {
+ Dispute dispute = openNewDisputeMessage.dispute;
+ if (isArbitrator(dispute)) {
+ if (!disputes.contains(dispute)) {
+ dispute.setStorage(getDisputeStorage());
+ disputes.add(dispute);
+ disputesObservableList.add(dispute);
+ sendPeerOpenedDisputeMessage(dispute);
+ } else {
+ log.warn("We got a dispute msg what we have already stored. TradeId = " + dispute.getTradeId());
+ }
+ } else {
+ log.error("Trader received openNewDisputeMessage. That must never happen.");
+ }
+ }
+
+ // not dispute requester receives that from arbitrator
+ private void onPeerOpenedDisputeMessage(PeerOpenedDisputeMessage peerOpenedDisputeMessage) {
+ Dispute dispute = peerOpenedDisputeMessage.dispute;
+ if (!isArbitrator(dispute)) {
+ Optional 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);
+ } else {
+ log.warn("We got a dispute msg what we have already stored. TradeId = " + dispute.getTradeId());
+ }
+ } else {
+ log.error("Arbitrator received peerOpenedDisputeMessage. That must never happen.");
+ }
+ }
+
+ // a trader can receive a msg from the arbitrator or the arbitrator form a trader. Trader to trader is not allowed.
+ private void onDisputeMailMessage(DisputeMailMessage disputeMailMessage) {
+ log.debug("onDisputeMailMessage " + disputeMailMessage);
+ Optional disputeOptional = findDispute(disputeMailMessage.getTradeId(), disputeMailMessage.getTraderId());
+ if (disputeOptional.isPresent()) {
+ Dispute dispute = disputeOptional.get();
+ if (!dispute.getDisputeMailMessagesAsObservableList().contains(disputeMailMessage))
+ dispute.addDisputeMessage(disputeMailMessage);
+ else
+ log.warn("We got a dispute mail msg what we have already stored. TradeId = " + disputeMailMessage.getTradeId());
+ } else {
+ log.warn("We got a dispute mail msg but we don't have a matching dispute. TradeId = " + disputeMailMessage.getTradeId());
+ }
+ }
+
+ // We get that message at both peers. The dispute object is in context of the trader
+ private void onDisputeResultMessage(DisputeResultMessage disputeResultMessage) {
+ DisputeResult disputeResult = disputeResultMessage.disputeResult;
+ if (!isArbitrator(disputeResult)) {
+ Optional disputeOptional = findDispute(disputeResult.tradeId, disputeResult.traderId);
+ if (disputeOptional.isPresent()) {
+ Dispute dispute = disputeOptional.get();
+
+ DisputeMailMessage disputeMailMessage = disputeResult.getResultMailMessage();
+ if (!dispute.getDisputeMailMessagesAsObservableList().contains(disputeMailMessage))
+ dispute.addDisputeMessage(disputeMailMessage);
+ else
+ log.warn("We got a dispute mail msg what we have already stored. TradeId = " + disputeMailMessage.getTradeId());
+
+ dispute.setIsClosed(true);
+ if (tradeManager.getTradeById(dispute.getTradeId()).isPresent())
+ tradeManager.closeDisputedTrade(dispute.getTradeId());
+ else {
+ Optional openOfferOptional = openOfferManager.getOpenOfferById(dispute.getTradeId());
+ if (openOfferOptional.isPresent())
+ openOfferManager.closeOpenOffer(openOfferOptional.get().getOffer());
+ }
+
+ if (dispute.disputeResultProperty().get() == null) {
+ dispute.setDisputeResult(disputeResult);
+
+ // We need to avoid publishing the tx from both traders as it would create problems with zero confirmation withdrawals
+ // There would be different transactions if both sign and publish (signers: once buyer+arb, once seller+arb)
+ // The tx publisher is the winner or in case both get 50% the buyer, as the buyer has more inventive to publish the tx as he receives
+ // more BTC as he has deposited
+ final Contract contract = dispute.getContract();
+
+ boolean isBuyer = keyRing.getPubKeyRing().equals(contract.getBuyerPubKeyRing());
+ if ((isBuyer && disputeResult.getWinner() == DisputeResult.Winner.BUYER)
+ || (!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.signAndFinalizeDisputedPayoutTx(
+ dispute.getDepositTxSerialized(),
+ disputeResult.getArbitratorSignature(),
+ disputeResult.getBuyerPayoutAmount(),
+ disputeResult.getSellerPayoutAmount(),
+ disputeResult.getArbitratorPayoutAmount(),
+ contract.getBuyerPayoutAddressString(),
+ contract.getSellerPayoutAddressString(),
+ disputeResult.getArbitratorAddressAsString(),
+ walletService.getAddressEntryByOfferId(dispute.getTradeId()),
+ contract.getBuyerBtcPubKey(),
+ contract.getSellerBtcPubKey(),
+ disputeResult.getArbitratorPubKey()
+ );
+ Transaction committedDisputedPayoutTx = tradeWalletService.addTransactionToWallet(signedDisputedPayoutTx);
+ log.debug("broadcast committedDisputedPayoutTx");
+ tradeWalletService.broadcastTx(committedDisputedPayoutTx, new FutureCallback() {
+ @Override
+ public void onSuccess(Transaction transaction) {
+ log.debug("BroadcastTx succeeded. Transaction:" + transaction);
+
+ // after successful publish we send peer the tx
+
+ sendPeerPublishedPayoutTxMessage(transaction, dispute, contract);
+ }
+
+ @Override
+ public void onFailure(@NotNull Throwable t) {
+ // TODO error handling
+ log.error(t.getMessage());
+ }
+ });
+ } catch (AddressFormatException | WalletException | TransactionVerificationException e) {
+ e.printStackTrace();
+ }
+ } else {
+ log.warn("DepositTx is null. TradeId = " + disputeResult.tradeId);
+ }
+ }
+ } else {
+ log.warn("We got a dispute msg what we have already stored. TradeId = " + disputeResult.tradeId);
+ }
+
+ /* DisputeMailMessage disputeMailMessage = disputeResult.getResultMailMessage();
+ if (!dispute.getDisputeMailMessagesAsObservableList().contains(disputeMailMessage))
+ dispute.addDisputeMessage(disputeMailMessage);
+ else
+ log.warn("We got a dispute mail msg what we have already stored. TradeId = " + disputeMailMessage.getTradeId());*/
+
+ } else {
+ log.warn("We got a dispute result msg but we don't have a matching dispute. TradeId = " + disputeResult.tradeId);
+ }
+ } else {
+ log.error("Arbitrator received disputeResultMessage. That must never happen.");
+ }
+ }
+
+ // losing trader or in case of 50/50 the seller gets the tx sent from the winner or buyer
+ private void onDisputedPayoutTxMessage(PeerPublishedPayoutTxMessage peerPublishedPayoutTxMessage) {
+ tradeWalletService.addTransactionToWallet(peerPublishedPayoutTxMessage.transaction);
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Getters
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ public Storage> getDisputeStorage() {
+ return disputeStorage;
+ }
+
+ public ObservableList getDisputesAsObservableList() {
+ return disputesObservableList;
+ }
+
+ public boolean isTrader(Dispute dispute) {
+ return keyRing.getPubKeyRing().equals(dispute.getTraderPubKeyRing());
+ }
+
+ private boolean isArbitrator(Dispute dispute) {
+ return keyRing.getPubKeyRing().equals(dispute.getArbitratorPubKeyRing());
+ }
+
+ private boolean isArbitrator(DisputeResult disputeResult) {
+ return walletService.getArbitratorAddressEntry().getAddressString().equals(disputeResult.getArbitratorAddressAsString());
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Utils
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ private Optional findDispute(String tradeId, int traderId) {
+ return disputes.stream().filter(e -> e.getTradeId().equals(tradeId) && e.getTraderId() == traderId).findFirst();
+ }
+
+ public Optional findOwnDispute(String tradeId) {
+ return disputes.stream().filter(e -> e.getTradeId().equals(tradeId)).findFirst();
+ }
+
+ public List findDisputesByTradeId(String tradeId) {
+ return disputes.stream().filter(e -> e.getTradeId().equals(tradeId)).collect(Collectors.toList());
+ }
+
+}
diff --git a/core/src/main/java/io/bitsquare/arbitration/DisputeResult.java b/core/src/main/java/io/bitsquare/arbitration/DisputeResult.java
new file mode 100644
index 0000000000..85f50a35d9
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/arbitration/DisputeResult.java
@@ -0,0 +1,265 @@
+/*
+ * 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 io.bitsquare.app.Version;
+import io.bitsquare.arbitration.messages.DisputeMailMessage;
+import javafx.beans.property.*;
+import org.bitcoinj.core.Coin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Date;
+
+public class DisputeResult implements Serializable {
+ // That object is sent over the wire, so we need to take care of version compatibility.
+ private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
+ transient private static final Logger log = LoggerFactory.getLogger(DisputeResult.class);
+
+ public enum FeePaymentPolicy {
+ LOSER,
+ SPLIT,
+ WAIVE
+ }
+
+ public enum Winner {
+ BUYER,
+ SELLER,
+ STALE_MATE
+ }
+
+ public final String tradeId;
+ public final int traderId;
+ private FeePaymentPolicy feePaymentPolicy;
+
+ private boolean tamperProofEvidence;
+ private boolean idVerification;
+ private boolean screenCast;
+ private String summaryNotes;
+ private DisputeMailMessage resultMailMessage;
+ private byte[] arbitratorSignature;
+ private long buyerPayoutAmount;
+ private long sellerPayoutAmount;
+ private long arbitratorPayoutAmount;
+ private String arbitratorAddressAsString;
+ private byte[] arbitratorPubKey;
+ private long closeDate;
+ private Winner winner;
+
+ transient private BooleanProperty tamperProofEvidenceProperty = new SimpleBooleanProperty();
+ transient private BooleanProperty idVerificationProperty = new SimpleBooleanProperty();
+ transient private BooleanProperty screenCastProperty = new SimpleBooleanProperty();
+ transient private ObjectProperty feePaymentPolicyProperty = new SimpleObjectProperty<>();
+ transient private StringProperty summaryNotesProperty = new SimpleStringProperty();
+
+ public DisputeResult(String tradeId, int traderId) {
+ this.tradeId = tradeId;
+ this.traderId = traderId;
+
+ feePaymentPolicy = FeePaymentPolicy.LOSER;
+ init();
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ try {
+ in.defaultReadObject();
+ init();
+ } catch (Throwable t) {
+ log.trace("Cannot be deserialized." + t.getMessage());
+ }
+ }
+
+ private void init() {
+ tamperProofEvidenceProperty = new SimpleBooleanProperty(tamperProofEvidence);
+ idVerificationProperty = new SimpleBooleanProperty(idVerification);
+ screenCastProperty = new SimpleBooleanProperty(screenCast);
+ feePaymentPolicyProperty = new SimpleObjectProperty<>(feePaymentPolicy);
+ summaryNotesProperty = new SimpleStringProperty(summaryNotes);
+
+ tamperProofEvidenceProperty.addListener((observable, oldValue, newValue) -> {
+ tamperProofEvidence = newValue;
+ });
+ idVerificationProperty.addListener((observable, oldValue, newValue) -> {
+ idVerification = newValue;
+ });
+ screenCastProperty.addListener((observable, oldValue, newValue) -> {
+ screenCast = newValue;
+ });
+ feePaymentPolicyProperty.addListener((observable, oldValue, newValue) -> {
+ feePaymentPolicy = newValue;
+ });
+ summaryNotesProperty.addListener((observable, oldValue, newValue) -> {
+ summaryNotes = newValue;
+ });
+ }
+
+ public BooleanProperty tamperProofEvidenceProperty() {
+ return tamperProofEvidenceProperty;
+ }
+
+ public BooleanProperty idVerificationProperty() {
+ return idVerificationProperty;
+ }
+
+ public BooleanProperty screenCastProperty() {
+ return screenCastProperty;
+ }
+
+ public void setFeePaymentPolicy(FeePaymentPolicy feePaymentPolicy) {
+ this.feePaymentPolicy = feePaymentPolicy;
+ feePaymentPolicyProperty.set(feePaymentPolicy);
+ }
+
+ public ReadOnlyObjectProperty feePaymentPolicyProperty() {
+ return feePaymentPolicyProperty;
+ }
+
+ public FeePaymentPolicy getFeePaymentPolicy() {
+ return feePaymentPolicy;
+ }
+
+
+ public StringProperty summaryNotesProperty() {
+ return summaryNotesProperty;
+ }
+
+ public void setResultMailMessage(DisputeMailMessage resultMailMessage) {
+ this.resultMailMessage = resultMailMessage;
+ }
+
+ public DisputeMailMessage getResultMailMessage() {
+ return resultMailMessage;
+ }
+
+ public void setArbitratorSignature(byte[] arbitratorSignature) {
+ this.arbitratorSignature = arbitratorSignature;
+ }
+
+ public byte[] getArbitratorSignature() {
+ return arbitratorSignature;
+ }
+
+ public void setBuyerPayoutAmount(Coin buyerPayoutAmount) {
+ this.buyerPayoutAmount = buyerPayoutAmount.value;
+ }
+
+ public Coin getBuyerPayoutAmount() {
+ return Coin.valueOf(buyerPayoutAmount);
+ }
+
+ public void setSellerPayoutAmount(Coin sellerPayoutAmount) {
+ this.sellerPayoutAmount = sellerPayoutAmount.value;
+ }
+
+ public Coin getSellerPayoutAmount() {
+ return Coin.valueOf(sellerPayoutAmount);
+ }
+
+ public void setArbitratorPayoutAmount(Coin arbitratorPayoutAmount) {
+ this.arbitratorPayoutAmount = arbitratorPayoutAmount.value;
+ }
+
+ public Coin getArbitratorPayoutAmount() {
+ return Coin.valueOf(arbitratorPayoutAmount);
+ }
+
+ public void setArbitratorAddressAsString(String arbitratorAddressAsString) {
+ this.arbitratorAddressAsString = arbitratorAddressAsString;
+ }
+
+ public String getArbitratorAddressAsString() {
+ return arbitratorAddressAsString;
+ }
+
+ public void setArbitratorPubKey(byte[] arbitratorPubKey) {
+ this.arbitratorPubKey = arbitratorPubKey;
+ }
+
+ public byte[] getArbitratorPubKey() {
+ return arbitratorPubKey;
+ }
+
+ public void setCloseDate(Date closeDate) {
+ this.closeDate = closeDate.getTime();
+ }
+
+ public Date getCloseDate() {
+ return new Date(closeDate);
+ }
+
+ public void setWinner(Winner winner) {
+ this.winner = winner;
+ }
+
+ public Winner getWinner() {
+ return winner;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof DisputeResult)) return false;
+
+ DisputeResult that = (DisputeResult) o;
+
+ if (traderId != that.traderId) return false;
+ if (tamperProofEvidence != that.tamperProofEvidence) return false;
+ if (idVerification != that.idVerification) return false;
+ if (screenCast != that.screenCast) return false;
+ if (buyerPayoutAmount != that.buyerPayoutAmount) return false;
+ if (sellerPayoutAmount != that.sellerPayoutAmount) return false;
+ if (arbitratorPayoutAmount != that.arbitratorPayoutAmount) return false;
+ if (closeDate != that.closeDate) return false;
+ if (tradeId != null ? !tradeId.equals(that.tradeId) : that.tradeId != null) return false;
+ if (feePaymentPolicy != that.feePaymentPolicy) return false;
+ if (summaryNotes != null ? !summaryNotes.equals(that.summaryNotes) : that.summaryNotes != null) return false;
+ if (resultMailMessage != null ? !resultMailMessage.equals(that.resultMailMessage) : that.resultMailMessage != null)
+ return false;
+ if (!Arrays.equals(arbitratorSignature, that.arbitratorSignature)) return false;
+ if (arbitratorAddressAsString != null ? !arbitratorAddressAsString.equals(that.arbitratorAddressAsString) : that.arbitratorAddressAsString != null)
+ return false;
+ if (!Arrays.equals(arbitratorPubKey, that.arbitratorPubKey)) return false;
+ return winner == that.winner;
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = tradeId != null ? tradeId.hashCode() : 0;
+ result = 31 * result + traderId;
+ result = 31 * result + (feePaymentPolicy != null ? feePaymentPolicy.hashCode() : 0);
+ result = 31 * result + (tamperProofEvidence ? 1 : 0);
+ result = 31 * result + (idVerification ? 1 : 0);
+ result = 31 * result + (screenCast ? 1 : 0);
+ result = 31 * result + (summaryNotes != null ? summaryNotes.hashCode() : 0);
+ result = 31 * result + (resultMailMessage != null ? resultMailMessage.hashCode() : 0);
+ result = 31 * result + (arbitratorSignature != null ? Arrays.hashCode(arbitratorSignature) : 0);
+ result = 31 * result + (int) (buyerPayoutAmount ^ (buyerPayoutAmount >>> 32));
+ result = 31 * result + (int) (sellerPayoutAmount ^ (sellerPayoutAmount >>> 32));
+ result = 31 * result + (int) (arbitratorPayoutAmount ^ (arbitratorPayoutAmount >>> 32));
+ result = 31 * result + (arbitratorAddressAsString != null ? arbitratorAddressAsString.hashCode() : 0);
+ result = 31 * result + (arbitratorPubKey != null ? Arrays.hashCode(arbitratorPubKey) : 0);
+ result = 31 * result + (int) (closeDate ^ (closeDate >>> 32));
+ result = 31 * result + (winner != null ? winner.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/arbitration/messages/DisputeMailMessage.java b/core/src/main/java/io/bitsquare/arbitration/messages/DisputeMailMessage.java
new file mode 100644
index 0000000000..e66fca6d7c
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/arbitration/messages/DisputeMailMessage.java
@@ -0,0 +1,237 @@
+/*
+ * 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.messages;
+
+import io.bitsquare.app.Version;
+import io.bitsquare.p2p.Address;
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+public class DisputeMailMessage implements DisputeMessage {
+ // That object is sent over the wire, so we need to take care of version compatibility.
+ private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
+ transient private static final Logger log = LoggerFactory.getLogger(DisputeMailMessage.class);
+
+ private final long date;
+ private final String tradeId;
+
+ private final int traderId;
+ private final boolean senderIsTrader;
+ private final String message;
+ private final List attachments = new ArrayList<>();
+ private boolean arrived;
+ private boolean storedInMailbox;
+ private boolean isSystemMessage;
+ private final Address myAddress;
+
+ transient private BooleanProperty arrivedProperty = new SimpleBooleanProperty();
+ transient private BooleanProperty storedInMailboxProperty = new SimpleBooleanProperty();
+
+ public DisputeMailMessage(String tradeId, int traderId, boolean senderIsTrader, String message, Address myAddress) {
+ this.tradeId = tradeId;
+ this.traderId = traderId;
+ this.senderIsTrader = senderIsTrader;
+ this.message = message;
+ this.myAddress = myAddress;
+ date = new Date().getTime();
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ try {
+ in.defaultReadObject();
+ arrivedProperty = new SimpleBooleanProperty(arrived);
+ storedInMailboxProperty = new SimpleBooleanProperty(storedInMailbox);
+ } catch (Throwable t) {
+ log.trace("Cannot be deserialized." + t.getMessage());
+ }
+ }
+
+ @Override
+ public Address getSenderAddress() {
+ return myAddress;
+ }
+
+ public void addAttachment(Attachment attachment) {
+ attachments.add(attachment);
+ }
+
+ public void addAllAttachments(List attachments) {
+ this.attachments.addAll(attachments);
+ }
+
+ public void setArrived(boolean arrived) {
+ this.arrived = arrived;
+ this.arrivedProperty.set(arrived);
+ }
+
+ public void setStoredInMailbox(boolean storedInMailbox) {
+ this.storedInMailbox = storedInMailbox;
+ this.storedInMailboxProperty.set(storedInMailbox);
+ }
+
+ public Date getDate() {
+ return new Date(date);
+ }
+
+ public boolean isSenderIsTrader() {
+ return senderIsTrader;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public int getTraderId() {
+ return traderId;
+ }
+
+ public BooleanProperty arrivedProperty() {
+ return arrivedProperty;
+ }
+
+ public BooleanProperty storedInMailboxProperty() {
+ return storedInMailboxProperty;
+ }
+
+ public List getAttachments() {
+ return attachments;
+ }
+
+ public String getTradeId() {
+ return tradeId;
+ }
+
+ public boolean isSystemMessage() {
+ return isSystemMessage;
+ }
+
+ public void setIsSystemMessage(boolean isSystemMessage) {
+ this.isSystemMessage = isSystemMessage;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof DisputeMailMessage)) return false;
+
+ DisputeMailMessage that = (DisputeMailMessage) o;
+
+ if (date != that.date) return false;
+ if (traderId != that.traderId) return false;
+ if (senderIsTrader != that.senderIsTrader) return false;
+ if (arrived != that.arrived) return false;
+ if (storedInMailbox != that.storedInMailbox) return false;
+ if (isSystemMessage != that.isSystemMessage) return false;
+ if (tradeId != null ? !tradeId.equals(that.tradeId) : that.tradeId != null) return false;
+ if (message != null ? !message.equals(that.message) : that.message != null) return false;
+ if (attachments != null ? !attachments.equals(that.attachments) : that.attachments != null) return false;
+ return !(myAddress != null ? !myAddress.equals(that.myAddress) : that.myAddress != null);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (int) (date ^ (date >>> 32));
+ result = 31 * result + (tradeId != null ? tradeId.hashCode() : 0);
+ result = 31 * result + traderId;
+ result = 31 * result + (senderIsTrader ? 1 : 0);
+ result = 31 * result + (message != null ? message.hashCode() : 0);
+ result = 31 * result + (attachments != null ? attachments.hashCode() : 0);
+ result = 31 * result + (arrived ? 1 : 0);
+ result = 31 * result + (storedInMailbox ? 1 : 0);
+ result = 31 * result + (isSystemMessage ? 1 : 0);
+ result = 31 * result + (myAddress != null ? myAddress.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "DisputeMailMessage{" +
+ "date=" + date +
+ ", tradeId='" + tradeId + '\'' +
+ ", traderId='" + traderId + '\'' +
+ ", senderIsTrader=" + senderIsTrader +
+ ", message='" + message + '\'' +
+ ", attachments=" + attachments +
+ '}';
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Static classes
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ public static class Attachment implements Serializable {
+ // That object is sent over the wire, so we need to take care of version compatibility.
+ private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
+ transient private static final Logger log = LoggerFactory.getLogger(Attachment.class);
+
+ private final byte[] bytes;
+ private final String fileName;
+
+ public Attachment(String fileName, byte[] bytes) {
+ this.fileName = fileName;
+ this.bytes = bytes;
+ }
+
+ public byte[] getBytes() {
+ return bytes;
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Attachment)) return false;
+
+ Attachment that = (Attachment) o;
+
+ if (!Arrays.equals(bytes, that.bytes)) return false;
+ return !(fileName != null ? !fileName.equals(that.fileName) : that.fileName != null);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = bytes != null ? Arrays.hashCode(bytes) : 0;
+ result = 31 * result + (fileName != null ? fileName.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "Attachment{" +
+ "description=" + fileName +
+ ", data=" + Arrays.toString(bytes) +
+ '}';
+ }
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/arbitration/messages/DisputeMessage.java b/core/src/main/java/io/bitsquare/arbitration/messages/DisputeMessage.java
new file mode 100644
index 0000000000..e676af9f3b
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/arbitration/messages/DisputeMessage.java
@@ -0,0 +1,23 @@
+/*
+ * 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.messages;
+
+import io.bitsquare.p2p.messaging.MailboxMessage;
+
+public interface DisputeMessage extends MailboxMessage {
+}
diff --git a/core/src/main/java/io/bitsquare/arbitration/messages/DisputeResultMessage.java b/core/src/main/java/io/bitsquare/arbitration/messages/DisputeResultMessage.java
new file mode 100644
index 0000000000..b111020bce
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/arbitration/messages/DisputeResultMessage.java
@@ -0,0 +1,60 @@
+/*
+ * 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.messages;
+
+import io.bitsquare.app.Version;
+import io.bitsquare.arbitration.DisputeResult;
+import io.bitsquare.p2p.Address;
+
+public class DisputeResultMessage implements DisputeMessage {
+ // That object is sent over the wire, so we need to take care of version compatibility.
+ private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
+
+ public final DisputeResult disputeResult;
+ private final Address myAddress;
+
+ public DisputeResultMessage(DisputeResult disputeResult, Address myAddress) {
+ this.disputeResult = disputeResult;
+ this.myAddress = myAddress;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof DisputeResultMessage)) return false;
+
+ DisputeResultMessage that = (DisputeResultMessage) o;
+
+ if (disputeResult != null ? !disputeResult.equals(that.disputeResult) : that.disputeResult != null)
+ return false;
+ return !(myAddress != null ? !myAddress.equals(that.myAddress) : that.myAddress != null);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = disputeResult != null ? disputeResult.hashCode() : 0;
+ result = 31 * result + (myAddress != null ? myAddress.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public Address getSenderAddress() {
+ return myAddress;
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/arbitration/messages/OpenNewDisputeMessage.java b/core/src/main/java/io/bitsquare/arbitration/messages/OpenNewDisputeMessage.java
new file mode 100644
index 0000000000..73545d3e48
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/arbitration/messages/OpenNewDisputeMessage.java
@@ -0,0 +1,59 @@
+/*
+ * 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.messages;
+
+import io.bitsquare.app.Version;
+import io.bitsquare.arbitration.Dispute;
+import io.bitsquare.p2p.Address;
+
+public class OpenNewDisputeMessage implements DisputeMessage {
+ // That object is sent over the wire, so we need to take care of version compatibility.
+ private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
+
+ public final Dispute dispute;
+ private final Address myAddress;
+
+ public OpenNewDisputeMessage(Dispute dispute, Address myAddress) {
+ this.dispute = dispute;
+ this.myAddress = myAddress;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof OpenNewDisputeMessage)) return false;
+
+ OpenNewDisputeMessage that = (OpenNewDisputeMessage) o;
+
+ if (dispute != null ? !dispute.equals(that.dispute) : that.dispute != null) return false;
+ return !(myAddress != null ? !myAddress.equals(that.myAddress) : that.myAddress != null);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = dispute != null ? dispute.hashCode() : 0;
+ result = 31 * result + (myAddress != null ? myAddress.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public Address getSenderAddress() {
+ return myAddress;
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/arbitration/messages/PeerOpenedDisputeMessage.java b/core/src/main/java/io/bitsquare/arbitration/messages/PeerOpenedDisputeMessage.java
new file mode 100644
index 0000000000..36ec20c33f
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/arbitration/messages/PeerOpenedDisputeMessage.java
@@ -0,0 +1,58 @@
+/*
+ * 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.messages;
+
+import io.bitsquare.app.Version;
+import io.bitsquare.arbitration.Dispute;
+import io.bitsquare.p2p.Address;
+
+public class PeerOpenedDisputeMessage implements DisputeMessage {
+ // That object is sent over the wire, so we need to take care of version compatibility.
+ private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
+ public final Dispute dispute;
+ private final Address myAddress;
+
+ public PeerOpenedDisputeMessage(Dispute dispute, Address myAddress) {
+ this.dispute = dispute;
+ this.myAddress = myAddress;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof PeerOpenedDisputeMessage)) return false;
+
+ PeerOpenedDisputeMessage that = (PeerOpenedDisputeMessage) o;
+
+ if (dispute != null ? !dispute.equals(that.dispute) : that.dispute != null) return false;
+ return !(myAddress != null ? !myAddress.equals(that.myAddress) : that.myAddress != null);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = dispute != null ? dispute.hashCode() : 0;
+ result = 31 * result + (myAddress != null ? myAddress.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public Address getSenderAddress() {
+ return myAddress;
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/arbitration/messages/PeerPublishedPayoutTxMessage.java b/core/src/main/java/io/bitsquare/arbitration/messages/PeerPublishedPayoutTxMessage.java
new file mode 100644
index 0000000000..be32f2cdad
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/arbitration/messages/PeerPublishedPayoutTxMessage.java
@@ -0,0 +1,65 @@
+/*
+ * 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.messages;
+
+import io.bitsquare.app.Version;
+import io.bitsquare.p2p.Address;
+
+import java.util.Arrays;
+
+public class PeerPublishedPayoutTxMessage implements DisputeMessage {
+ // That object is sent over the wire, so we need to take care of version compatibility.
+ private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
+
+ public final byte[] transaction;
+ public final String tradeId;
+ private final Address myAddress;
+
+ public PeerPublishedPayoutTxMessage(byte[] transaction, String tradeId, Address myAddress) {
+ this.transaction = transaction;
+ this.tradeId = tradeId;
+ this.myAddress = myAddress;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof PeerPublishedPayoutTxMessage)) return false;
+
+ PeerPublishedPayoutTxMessage that = (PeerPublishedPayoutTxMessage) o;
+
+ if (!Arrays.equals(transaction, that.transaction)) return false;
+ if (tradeId != null ? !tradeId.equals(that.tradeId) : that.tradeId != null) return false;
+ return !(myAddress != null ? !myAddress.equals(that.myAddress) : that.myAddress != null);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = transaction != null ? Arrays.hashCode(transaction) : 0;
+ result = 31 * result + (tradeId != null ? tradeId.hashCode() : 0);
+ result = 31 * result + (myAddress != null ? myAddress.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public Address getSenderAddress() {
+ return myAddress;
+ }
+
+}
diff --git a/core/src/main/java/io/bitsquare/btc/data/InputsAndChangeOutput.java b/core/src/main/java/io/bitsquare/btc/data/InputsAndChangeOutput.java
new file mode 100644
index 0000000000..f171d7d7a5
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/btc/data/InputsAndChangeOutput.java
@@ -0,0 +1,40 @@
+/*
+ * 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.btc.data;
+
+import javax.annotation.Nullable;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+public class InputsAndChangeOutput {
+ public final List rawInputs;
+
+ // Is set to 0L in case we don't have an output
+ public final long changeOutputValue;
+ @Nullable
+ public final String changeOutputAddress;
+
+ public InputsAndChangeOutput(List rawInputs, long changeOutputValue, @Nullable String changeOutputAddress) {
+ checkArgument(!rawInputs.isEmpty(), "rawInputs.isEmpty()");
+
+ this.rawInputs = rawInputs;
+ this.changeOutputValue = changeOutputValue;
+ this.changeOutputAddress = changeOutputAddress;
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/btc/data/PreparedDepositTxAndOffererInputs.java b/core/src/main/java/io/bitsquare/btc/data/PreparedDepositTxAndOffererInputs.java
new file mode 100644
index 0000000000..3e6e00ab6a
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/btc/data/PreparedDepositTxAndOffererInputs.java
@@ -0,0 +1,30 @@
+/*
+ * 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.btc.data;
+
+import java.util.List;
+
+public class PreparedDepositTxAndOffererInputs {
+ public final List rawOffererInputs;
+ public final byte[] depositTransaction;
+
+ public PreparedDepositTxAndOffererInputs(List rawOffererInputs, byte[] depositTransaction) {
+ this.rawOffererInputs = rawOffererInputs;
+ this.depositTransaction = depositTransaction;
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/btc/data/RawInput.java b/core/src/main/java/io/bitsquare/btc/data/RawInput.java
new file mode 100644
index 0000000000..b4da3d320d
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/btc/data/RawInput.java
@@ -0,0 +1,59 @@
+/*
+ * 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.btc.data;
+
+import io.bitsquare.app.Version;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+public class RawInput implements Serializable {
+ // That object is sent over the wire, so we need to take care of version compatibility.
+ private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
+
+ public final long index;
+ public final byte[] parentTransaction;
+ public final long value;
+
+ public RawInput(long index, byte[] parentTransaction, long value) {
+ this.index = index;
+ this.parentTransaction = parentTransaction;
+ this.value = value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof RawInput)) return false;
+
+ RawInput rawInput = (RawInput) o;
+
+ if (index != rawInput.index) return false;
+ if (value != rawInput.value) return false;
+ return Arrays.equals(parentTransaction, rawInput.parentTransaction);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (int) (index ^ (index >>> 32));
+ result = 31 * result + (parentTransaction != null ? Arrays.hashCode(parentTransaction) : 0);
+ result = 31 * result + (int) (value ^ (value >>> 32));
+ return result;
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/locale/CryptoCurrency.java b/core/src/main/java/io/bitsquare/locale/CryptoCurrency.java
new file mode 100644
index 0000000000..436813ad52
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/locale/CryptoCurrency.java
@@ -0,0 +1,36 @@
+/*
+ * 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.locale;
+
+import io.bitsquare.app.Version;
+
+import java.io.Serializable;
+
+public class CryptoCurrency extends TradeCurrency implements Serializable {
+ // That object is saved to disc. We need to take care of changes to not break deserialization.
+ private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
+
+ public CryptoCurrency(String currencyCode, String name) {
+ super(currencyCode, name);
+ }
+
+ public CryptoCurrency(String currencyCode, String name, String symbol) {
+ super(currencyCode, name, symbol);
+ }
+
+}
diff --git a/core/src/main/java/io/bitsquare/locale/FiatCurrency.java b/core/src/main/java/io/bitsquare/locale/FiatCurrency.java
new file mode 100644
index 0000000000..2c59b70600
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/locale/FiatCurrency.java
@@ -0,0 +1,54 @@
+/*
+ * 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.locale;
+
+import io.bitsquare.app.Version;
+import io.bitsquare.user.Preferences;
+
+import java.io.Serializable;
+import java.util.Currency;
+
+public class FiatCurrency extends TradeCurrency implements Serializable {
+ // That object is saved to disc. We need to take care of changes to not break deserialization.
+ private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
+
+ private final Currency currency;
+
+ public FiatCurrency(String currencyCode) {
+ this(Currency.getInstance(currencyCode));
+ }
+
+ public FiatCurrency(Currency currency) {
+ super(currency.getCurrencyCode(), currency.getDisplayName(Preferences.getDefaultLocale()), currency.getSymbol());
+ this.currency = currency;
+ }
+
+ public Currency getCurrency() {
+ return currency;
+ }
+
+ @Override
+ public String toString() {
+ return "FiatCurrency{" +
+ "currency=" + currency +
+ ", code='" + code + '\'' +
+ ", name='" + name + '\'' +
+ ", symbol='" + symbol + '\'' +
+ '}';
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/locale/TradeCurrency.java b/core/src/main/java/io/bitsquare/locale/TradeCurrency.java
new file mode 100644
index 0000000000..4bd0c07df9
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/locale/TradeCurrency.java
@@ -0,0 +1,89 @@
+/*
+ * 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.locale;
+
+import io.bitsquare.app.Version;
+
+import java.io.Serializable;
+
+public class TradeCurrency implements Serializable {
+ // That object is saved to disc. We need to take care of changes to not break deserialization.
+ private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
+
+ protected final String code;
+ protected final String name;
+ protected String symbol;
+
+
+ public TradeCurrency(String code) {
+ this.code = code;
+ this.name = CurrencyUtil.getNameByCode(code);
+ }
+
+ protected TradeCurrency(String code, String name) {
+ this.code = code;
+ this.name = name;
+ }
+
+ public TradeCurrency(String code, String name, String symbol) {
+ this.code = code;
+ this.name = name;
+ this.symbol = symbol;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getSymbol() {
+ return symbol;
+ }
+
+ public String getCodeAndName() {
+ return code + " (" + name + ")";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof TradeCurrency)) return false;
+
+ TradeCurrency that = (TradeCurrency) o;
+
+ return !(getCode() != null ? !getCode().equals(that.getCode()) : that.getCode() != null);
+
+ }
+
+ @Override
+ public int hashCode() {
+ return getCode() != null ? getCode().hashCode() : 0;
+ }
+
+ @Override
+ public String toString() {
+ return "TradeCurrency{" +
+ "code='" + code + '\'' +
+ ", name='" + name + '\'' +
+ ", symbol='" + symbol + '\'' +
+ '}';
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/payment/AliPayAccount.java b/core/src/main/java/io/bitsquare/payment/AliPayAccount.java
new file mode 100644
index 0000000000..af6e35fd37
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/payment/AliPayAccount.java
@@ -0,0 +1,43 @@
+/*
+ * 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.payment;
+
+import io.bitsquare.app.Version;
+import io.bitsquare.locale.FiatCurrency;
+
+import java.io.Serializable;
+
+public class AliPayAccount extends PaymentAccount implements Serializable {
+ // That object is saved to disc. We need to take care of changes to not break deserialization.
+ private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
+
+ public AliPayAccount() {
+ super(PaymentMethod.ALI_PAY);
+ setSingleTradeCurrency(new FiatCurrency("CNY"));
+
+ contractData = new AliPayAccountContractData(paymentMethod.getId(), id, paymentMethod.getMaxTradePeriod());
+ }
+
+ public void setAccountNr(String accountNr) {
+ ((AliPayAccountContractData) contractData).setAccountNr(accountNr);
+ }
+
+ public String getAccountNr() {
+ return ((AliPayAccountContractData) contractData).getAccountNr();
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/payment/AliPayAccountContractData.java b/core/src/main/java/io/bitsquare/payment/AliPayAccountContractData.java
new file mode 100644
index 0000000000..d6de629953
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/payment/AliPayAccountContractData.java
@@ -0,0 +1,53 @@
+/*
+ * 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.payment;
+
+import io.bitsquare.app.Version;
+
+import java.io.Serializable;
+
+public class AliPayAccountContractData extends PaymentAccountContractData implements Serializable {
+ // That object is sent over the wire, so we need to take care of version compatibility.
+ private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
+
+ private String accountNr;
+
+ public AliPayAccountContractData(String paymentMethod, String id, int maxTradePeriod) {
+ super(paymentMethod, id, maxTradePeriod);
+ }
+
+ public void setAccountNr(String accountNr) {
+ this.accountNr = accountNr;
+ }
+
+ public String getAccountNr() {
+ return accountNr;
+ }
+
+ @Override
+ public String getPaymentDetails() {
+ return "AliPay - Account nr.: " + accountNr;
+ }
+
+ @Override
+ public String toString() {
+ return "AliPayAccountContractData{" +
+ "accountNr='" + accountNr + '\'' +
+ '}';
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/payment/BlockChainAccount.java b/core/src/main/java/io/bitsquare/payment/BlockChainAccount.java
new file mode 100644
index 0000000000..d39c4e4de4
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/payment/BlockChainAccount.java
@@ -0,0 +1,42 @@
+/*
+ * 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.payment;
+
+import io.bitsquare.app.Version;
+
+import java.io.Serializable;
+
+public class BlockChainAccount extends PaymentAccount implements Serializable {
+ // That object is saved to disc. We need to take care of changes to not break deserialization.
+ private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
+
+
+ public BlockChainAccount() {
+ super(PaymentMethod.BLOCK_CHAINS);
+
+ contractData = new BlockChainAccountContractData(paymentMethod.getId(), id, paymentMethod.getMaxTradePeriod());
+ }
+
+ public void setAddress(String address) {
+ ((BlockChainAccountContractData) contractData).setAddress(address);
+ }
+
+ public String getAddress() {
+ return ((BlockChainAccountContractData) contractData).getAddress();
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/payment/BlockChainAccountContractData.java b/core/src/main/java/io/bitsquare/payment/BlockChainAccountContractData.java
new file mode 100644
index 0000000000..37435e162b
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/payment/BlockChainAccountContractData.java
@@ -0,0 +1,55 @@
+/*
+ * 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.payment;
+
+import io.bitsquare.app.Version;
+
+import java.io.Serializable;
+
+public class BlockChainAccountContractData extends PaymentAccountContractData implements Serializable {
+ // That object is sent over the wire, so we need to take care of version compatibility.
+ private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
+
+ private String address;
+ private String paymentId;
+
+ public BlockChainAccountContractData(String paymentMethod, String id, int maxTradePeriod) {
+ super(paymentMethod, id, maxTradePeriod);
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ @Override
+ public String getPaymentDetails() {
+ return "Address: " + address;
+ }
+
+ public void setPaymentId(String paymentId) {
+ this.paymentId = paymentId;
+ }
+
+ public String getPaymentId() {
+ return paymentId;
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/payment/OKPayAccount.java b/core/src/main/java/io/bitsquare/payment/OKPayAccount.java
new file mode 100644
index 0000000000..08eadf4d14
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/payment/OKPayAccount.java
@@ -0,0 +1,46 @@
+/*
+ * 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.payment;
+
+import io.bitsquare.app.Version;
+import io.bitsquare.locale.CurrencyUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.Serializable;
+
+public class OKPayAccount extends PaymentAccount implements Serializable {
+ // That object is saved to disc. We need to take care of changes to not break deserialization.
+ private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
+
+ private static final Logger log = LoggerFactory.getLogger(OKPayAccount.class);
+
+ public OKPayAccount() {
+ super(PaymentMethod.OK_PAY);
+ tradeCurrencies.addAll(CurrencyUtil.getAllOKPayCurrencies());
+ contractData = new OKPayAccountContractData(paymentMethod.getId(), id, paymentMethod.getMaxTradePeriod());
+ }
+
+ public void setAccountNr(String accountNr) {
+ ((OKPayAccountContractData) contractData).setAccountNr(accountNr);
+ }
+
+ public String getAccountNr() {
+ return ((OKPayAccountContractData) contractData).getAccountNr();
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/payment/OKPayAccountContractData.java b/core/src/main/java/io/bitsquare/payment/OKPayAccountContractData.java
new file mode 100644
index 0000000000..8f111c97e9
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/payment/OKPayAccountContractData.java
@@ -0,0 +1,48 @@
+/*
+ * 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.payment;
+
+import io.bitsquare.app.Version;
+
+import java.io.Serializable;
+
+public class OKPayAccountContractData extends PaymentAccountContractData implements Serializable {
+ // That object is sent over the wire, so we need to take care of version compatibility.
+ private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
+
+ private String accountNr;
+
+ public OKPayAccountContractData(String paymentMethod, String id, int maxTradePeriod) {
+ super(paymentMethod, id, maxTradePeriod);
+ }
+
+ public void setAccountNr(String accountNr) {
+ this.accountNr = accountNr;
+ }
+
+ public String getAccountNr() {
+ return accountNr;
+ }
+
+ @Override
+ public String getPaymentDetails() {
+ return "OKPay - Account nr.: " + accountNr;
+ }
+
+
+}
diff --git a/core/src/main/java/io/bitsquare/payment/PaymentAccount.java b/core/src/main/java/io/bitsquare/payment/PaymentAccount.java
new file mode 100644
index 0000000000..ee9f68efcd
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/payment/PaymentAccount.java
@@ -0,0 +1,168 @@
+/*
+ * 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.payment;
+
+import io.bitsquare.app.Version;
+import io.bitsquare.locale.Country;
+import io.bitsquare.locale.TradeCurrency;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nullable;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+public class PaymentAccount implements Serializable {
+ // That object is saved to disc. We need to take care of changes to not break deserialization.
+ private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
+
+ private static final Logger log = LoggerFactory.getLogger(PaymentAccount.class);
+
+ protected final String id;
+ protected final PaymentMethod paymentMethod;
+ protected String accountName;
+ protected final List tradeCurrencies = new ArrayList<>();
+ protected TradeCurrency selectedTradeCurrency;
+ @Nullable
+ protected Country country = null;
+ protected PaymentAccountContractData contractData;
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Constructor
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+
+ public PaymentAccount(PaymentMethod paymentMethod) {
+ this.paymentMethod = paymentMethod;
+ id = UUID.randomUUID().toString();
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // API
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ public void addCurrency(TradeCurrency tradeCurrency) {
+ if (!tradeCurrencies.contains(tradeCurrency))
+ tradeCurrencies.add(tradeCurrency);
+ }
+
+ public void removeCurrency(TradeCurrency tradeCurrency) {
+ if (tradeCurrencies.contains(tradeCurrency))
+ tradeCurrencies.remove(tradeCurrency);
+ }
+
+ public boolean hasMultipleCurrencies() {
+ return tradeCurrencies.size() > 1;
+ }
+
+ public void setSingleTradeCurrency(TradeCurrency tradeCurrency) {
+ tradeCurrencies.clear();
+ tradeCurrencies.add(tradeCurrency);
+ setSelectedTradeCurrency(tradeCurrency);
+ }
+
+ public TradeCurrency getSingleTradeCurrency() {
+ if (!tradeCurrencies.isEmpty())
+ return tradeCurrencies.get(0);
+ else
+ return null;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Getter, Setter
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ public String getAccountName() {
+ return accountName;
+ }
+
+ public void setAccountName(String accountName) {
+ this.accountName = accountName;
+ }
+
+ @Nullable
+ public Country getCountry() {
+ return country;
+ }
+
+ public void setCountry(@Nullable Country country) {
+ this.country = country;
+ contractData.setCountryCode(country.code);
+ }
+
+ public void setSelectedTradeCurrency(TradeCurrency tradeCurrency) {
+ selectedTradeCurrency = tradeCurrency;
+ }
+
+ public TradeCurrency getSelectedTradeCurrency() {
+ return selectedTradeCurrency;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Getter
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ public String getId() {
+ return id;
+ }
+
+ public PaymentMethod getPaymentMethod() {
+ return paymentMethod;
+ }
+
+ public List getTradeCurrencies() {
+ return tradeCurrencies;
+ }
+
+ public PaymentAccountContractData getContractData() {
+ return contractData;
+ }
+
+ public String getPaymentDetails() {
+ return contractData.getPaymentDetails();
+ }
+
+ public int getMaxTradePeriod() {
+ return contractData.getMaxTradePeriod();
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Util
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public String toString() {
+ return contractData.toString() + '\'' +
+ "PaymentAccount{" +
+ "id='" + id + '\'' +
+ ", paymentMethod=" + paymentMethod +
+ ", accountName='" + accountName + '\'' +
+ ", tradeCurrencies=" + tradeCurrencies +
+ ", selectedTradeCurrency=" + selectedTradeCurrency +
+ ", country=" + country +
+ ", contractData=" + contractData +
+ '}';
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/payment/PaymentAccountContractData.java b/core/src/main/java/io/bitsquare/payment/PaymentAccountContractData.java
new file mode 100644
index 0000000000..ddf4a93e7c
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/payment/PaymentAccountContractData.java
@@ -0,0 +1,102 @@
+/*
+ * 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.payment;
+
+import io.bitsquare.app.Version;
+
+import javax.annotation.Nullable;
+import java.io.Serializable;
+
+public abstract class PaymentAccountContractData implements Serializable {
+ // That object is sent over the wire, so we need to take care of version compatibility.
+ private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
+
+ private final String paymentMethodName;
+ private final String id;
+ private final int maxTradePeriod;
+
+ @Nullable
+ private String countryCode;
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Constructor
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ public PaymentAccountContractData(String paymentMethodName, String id, int maxTradePeriod) {
+ this.paymentMethodName = paymentMethodName;
+ this.id = id;
+ this.maxTradePeriod = maxTradePeriod;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Getter, Setter
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ public void setCountryCode(String countryCode) {
+ this.countryCode = countryCode;
+ }
+
+ @Nullable
+ public String getCountryCode() {
+ return countryCode;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Getter
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ public String getId() {
+ return id;
+ }
+
+ public String getPaymentMethodName() {
+ return paymentMethodName;
+ }
+
+ abstract public String getPaymentDetails();
+
+ public int getMaxTradePeriod() {
+ return maxTradePeriod;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof PaymentAccountContractData)) return false;
+
+ PaymentAccountContractData that = (PaymentAccountContractData) o;
+
+ if (maxTradePeriod != that.maxTradePeriod) return false;
+ if (paymentMethodName != null ? !paymentMethodName.equals(that.paymentMethodName) : that.paymentMethodName != null)
+ return false;
+ if (id != null ? !id.equals(that.id) : that.id != null) return false;
+ return !(countryCode != null ? !countryCode.equals(that.countryCode) : that.countryCode != null);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = paymentMethodName != null ? paymentMethodName.hashCode() : 0;
+ result = 31 * result + (id != null ? id.hashCode() : 0);
+ result = 31 * result + maxTradePeriod;
+ result = 31 * result + (countryCode != null ? countryCode.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/payment/PaymentMethod.java b/core/src/main/java/io/bitsquare/payment/PaymentMethod.java
new file mode 100644
index 0000000000..8376715b18
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/payment/PaymentMethod.java
@@ -0,0 +1,144 @@
+/*
+ * 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.payment;
+
+import io.bitsquare.app.Version;
+import org.jetbrains.annotations.NotNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+// Don't use Enum as it breaks serialisation when changing entries and we want to stay flexible here
+public class PaymentMethod implements Serializable, Comparable {
+ // That object is saved to disc. We need to take care of changes to not break deserialization.
+ private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
+
+ protected final Logger log = LoggerFactory.getLogger(this.getClass());
+
+ // time in blocks (average 10 min for one block confirmation
+ private static final int HOUR = 6;
+ private static final int DAY = HOUR * 24;
+
+ public static final String OK_PAY_ID = "OK_PAY";
+ public static final String PERFECT_MONEY_ID = "PERFECT_MONEY";
+ public static final String SEPA_ID = "SEPA";
+ public static final String SWISH_ID = "SWISH";
+ public static final String ALI_PAY_ID = "ALI_PAY";
+ /* public static final String FED_WIRE="FED_WIRE";*/
+ /* public static final String TRANSFER_WISE="TRANSFER_WISE";*/
+ /* public static final String US_POSTAL_MONEY_ORDER="US_POSTAL_MONEY_ORDER";*/
+ public static final String BLOCK_CHAINS_ID = "BLOCK_CHAINS";
+
+ public static PaymentMethod OK_PAY;
+ public static PaymentMethod PERFECT_MONEY;
+ public static PaymentMethod SEPA;
+ public static PaymentMethod SWISH;
+ public static PaymentMethod ALI_PAY;
+ /* public static PaymentMethod FED_WIRE;*/
+ /* public static PaymentMethod TRANSFER_WISE;*/
+ /* public static PaymentMethod US_POSTAL_MONEY_ORDER;*/
+ public static PaymentMethod BLOCK_CHAINS;
+
+ public static final List ALL_VALUES = new ArrayList<>(Arrays.asList(
+ OK_PAY = new PaymentMethod(OK_PAY_ID, 0, HOUR), // tx instant so min. wait time
+ PERFECT_MONEY = new PaymentMethod(PERFECT_MONEY_ID, 0, DAY),
+ SEPA = new PaymentMethod(SEPA_ID, 0, 8 * DAY), // sepa takes 1-3 business days. We use 8 days to include safety for holidays
+ SWISH = new PaymentMethod(SWISH_ID, 0, DAY),
+ ALI_PAY = new PaymentMethod(ALI_PAY_ID, 0, DAY),
+ /* FED_WIRE = new PaymentMethod(FED_WIRE_ID, 0, DAY),*/
+ /* TRANSFER_WISE = new PaymentMethod(TRANSFER_WISE_ID, 0, DAY),*/
+ /* US_POSTAL_MONEY_ORDER = new PaymentMethod(US_POSTAL_MONEY_ORDER_ID, 0, DAY),*/
+ BLOCK_CHAINS = new PaymentMethod(BLOCK_CHAINS_ID, 0, DAY)
+ ));
+
+
+ private final String id;
+
+ private final long lockTime;
+
+ private final int maxTradePeriod;
+
+ /**
+ * @param id
+ * @param lockTime lock time when seller release BTC until the payout tx gets valid (bitcoin tx lockTime). Serves as protection
+ * against charge back risk. If Bank do the charge back quickly the Arbitrator and the seller can push another
+ * double spend tx to invalidate the time locked payout tx. For the moment we set all to 0 but will have it in
+ * place when needed.
+ * @param maxTradePeriod The min. period a trader need to wait until he gets displayed the contact form for opening a dispute.
+ */
+ public PaymentMethod(String id, long lockTime, int maxTradePeriod) {
+ this.id = id;
+ this.lockTime = lockTime;
+ this.maxTradePeriod = maxTradePeriod;
+ }
+
+ public static PaymentMethod getPaymentMethodByName(String name) {
+ return ALL_VALUES.stream().filter(e -> e.getId().equals(name)).findFirst().get();
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public int getMaxTradePeriod() {
+ return maxTradePeriod;
+ }
+
+ public long getLockTime() {
+ return lockTime;
+ }
+
+ @Override
+ public int compareTo(@NotNull Object other) {
+ return this.id.compareTo(((PaymentMethod) other).id);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof PaymentMethod)) return false;
+
+ PaymentMethod that = (PaymentMethod) o;
+
+ if (getLockTime() != that.getLockTime()) return false;
+ if (getMaxTradePeriod() != that.getMaxTradePeriod()) return false;
+ return !(getId() != null ? !getId().equals(that.getId()) : that.getId() != null);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = getId() != null ? getId().hashCode() : 0;
+ result = 31 * result + (int) (getLockTime() ^ (getLockTime() >>> 32));
+ result = 31 * result + getMaxTradePeriod();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "PaymentMethod{" +
+ "name='" + id + '\'' +
+ ", lockTime=" + lockTime +
+ ", waitPeriodForOpenDispute=" + maxTradePeriod +
+ '}';
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/payment/PerfectMoneyAccount.java b/core/src/main/java/io/bitsquare/payment/PerfectMoneyAccount.java
new file mode 100644
index 0000000000..46b2521cd8
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/payment/PerfectMoneyAccount.java
@@ -0,0 +1,51 @@
+/*
+ * 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.payment;
+
+import io.bitsquare.app.Version;
+import io.bitsquare.locale.FiatCurrency;
+
+import java.io.Serializable;
+
+public class PerfectMoneyAccount extends PaymentAccount implements Serializable {
+ // That object is saved to disc. We need to take care of changes to not break deserialization.
+ private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
+
+ public PerfectMoneyAccount() {
+ super(PaymentMethod.PERFECT_MONEY);
+ setSingleTradeCurrency(new FiatCurrency("USD"));
+
+ contractData = new PerfectMoneyAccountContractData(paymentMethod.getId(), id, paymentMethod.getMaxTradePeriod());
+ }
+
+ public String getHolderName() {
+ return ((PerfectMoneyAccountContractData) contractData).getHolderName();
+ }
+
+ public void setHolderName(String holderName) {
+ ((PerfectMoneyAccountContractData) contractData).setHolderName(holderName);
+ }
+
+ public void setAccountNr(String accountNr) {
+ ((PerfectMoneyAccountContractData) contractData).setAccountNr(accountNr);
+ }
+
+ public String getAccountNr() {
+ return ((PerfectMoneyAccountContractData) contractData).getAccountNr();
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/payment/PerfectMoneyAccountContractData.java b/core/src/main/java/io/bitsquare/payment/PerfectMoneyAccountContractData.java
new file mode 100644
index 0000000000..4161e79e62
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/payment/PerfectMoneyAccountContractData.java
@@ -0,0 +1,56 @@
+/*
+ * 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.payment;
+
+import io.bitsquare.app.Version;
+
+import java.io.Serializable;
+
+public class PerfectMoneyAccountContractData extends PaymentAccountContractData implements Serializable {
+ // That object is sent over the wire, so we need to take care of version compatibility.
+ private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
+
+ private String holderName;
+ private String accountNr;
+
+ public PerfectMoneyAccountContractData(String paymentMethod, String id, int maxTradePeriod) {
+ super(paymentMethod, id, maxTradePeriod);
+ }
+
+ public String getHolderName() {
+ return holderName;
+ }
+
+ public void setHolderName(String holderName) {
+ this.holderName = holderName;
+ }
+
+ public void setAccountNr(String accountNr) {
+ this.accountNr = accountNr;
+ }
+
+ public String getAccountNr() {
+ return accountNr;
+ }
+
+ @Override
+ public String getPaymentDetails() {
+ return "PerfectMoney - Holder name: " + holderName + ", account nr.: " + accountNr;
+ }
+
+}
diff --git a/core/src/main/java/io/bitsquare/payment/SepaAccount.java b/core/src/main/java/io/bitsquare/payment/SepaAccount.java
new file mode 100644
index 0000000000..835e5dec2c
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/payment/SepaAccount.java
@@ -0,0 +1,71 @@
+/*
+ * 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.payment;
+
+import io.bitsquare.app.Version;
+
+import java.io.Serializable;
+import java.util.List;
+
+public class SepaAccount extends PaymentAccount implements Serializable {
+ // That object is saved to disc. We need to take care of changes to not break deserialization.
+ private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
+
+ public SepaAccount() {
+ super(PaymentMethod.SEPA);
+
+ contractData = new SepaAccountContractData(paymentMethod.getId(), id, paymentMethod.getMaxTradePeriod());
+ }
+
+ public void setHolderName(String holderName) {
+ ((SepaAccountContractData) contractData).setHolderName(holderName);
+ }
+
+ public String getHolderName() {
+ return ((SepaAccountContractData) contractData).getHolderName();
+ }
+
+ public void setIban(String iban) {
+ ((SepaAccountContractData) contractData).setIban(iban);
+ }
+
+ public String getIban() {
+ return ((SepaAccountContractData) contractData).getIban();
+ }
+
+ public void setBic(String bic) {
+ ((SepaAccountContractData) contractData).setBic(bic);
+ }
+
+ public String getBic() {
+ return ((SepaAccountContractData) contractData).getBic();
+ }
+
+ public List getAcceptedCountryCodes() {
+ return ((SepaAccountContractData) contractData).getAcceptedCountryCodes();
+ }
+
+ public void addAcceptedCountry(String countryCode) {
+ ((SepaAccountContractData) contractData).addAcceptedCountry(countryCode);
+ }
+
+ public void removeAcceptedCountry(String countryCode) {
+ ((SepaAccountContractData) contractData).removeAcceptedCountry(countryCode);
+ }
+
+}
diff --git a/core/src/main/java/io/bitsquare/payment/SepaAccountContractData.java b/core/src/main/java/io/bitsquare/payment/SepaAccountContractData.java
new file mode 100644
index 0000000000..a50aeb2eea
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/payment/SepaAccountContractData.java
@@ -0,0 +1,89 @@
+/*
+ * 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.payment;
+
+import io.bitsquare.app.Version;
+import io.bitsquare.locale.CountryUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class SepaAccountContractData extends PaymentAccountContractData implements Serializable {
+ // That object is sent over the wire, so we need to take care of version compatibility.
+ private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
+
+ transient private static final Logger log = LoggerFactory.getLogger(SepaAccountContractData.class);
+
+ private String holderName;
+ private String iban;
+ private String bic;
+ private Set acceptedCountryCodes;
+
+ public SepaAccountContractData(String paymentMethod, String id, int maxTradePeriod) {
+ super(paymentMethod, id, maxTradePeriod);
+ acceptedCountryCodes = CountryUtil.getAllSepaCountries().stream().map(e -> e.code).collect(Collectors.toSet());
+ }
+
+ public void setHolderName(String holderName) {
+ this.holderName = holderName;
+ }
+
+ public String getHolderName() {
+ return holderName;
+ }
+
+ public void setIban(String iban) {
+ this.iban = iban;
+ }
+
+ public String getIban() {
+ return iban;
+ }
+
+ public void setBic(String bic) {
+ this.bic = bic;
+ }
+
+ public String getBic() {
+ return bic;
+ }
+
+ public void addAcceptedCountry(String countryCode) {
+ acceptedCountryCodes.add(countryCode);
+ }
+
+ public void removeAcceptedCountry(String countryCode) {
+ acceptedCountryCodes.remove(countryCode);
+ }
+
+ public List getAcceptedCountryCodes() {
+ List sortedList = new ArrayList<>(acceptedCountryCodes);
+ sortedList.sort((a, b) -> a.compareTo(b));
+ return sortedList;
+ }
+
+ @Override
+ public String getPaymentDetails() {
+ return "SEPA - Holder name: " + holderName + ", IBAN: " + iban + ", BIC: " + bic + ", country code: " + getCountryCode();
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/payment/SwishAccount.java b/core/src/main/java/io/bitsquare/payment/SwishAccount.java
new file mode 100644
index 0000000000..6f81a9fbe6
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/payment/SwishAccount.java
@@ -0,0 +1,51 @@
+/*
+ * 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.payment;
+
+import io.bitsquare.app.Version;
+import io.bitsquare.locale.FiatCurrency;
+
+import java.io.Serializable;
+
+public class SwishAccount extends PaymentAccount implements Serializable {
+ // That object is saved to disc. We need to take care of changes to not break deserialization.
+ private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
+
+ public SwishAccount() {
+ super(PaymentMethod.SWISH);
+ setSingleTradeCurrency(new FiatCurrency("SEK"));
+
+ contractData = new SwishAccountContractData(paymentMethod.getId(), id, paymentMethod.getMaxTradePeriod());
+ }
+
+ public void setMobileNr(String mobileNr) {
+ ((SwishAccountContractData) contractData).setMobileNr(mobileNr);
+ }
+
+ public String getMobileNr() {
+ return ((SwishAccountContractData) contractData).getMobileNr();
+ }
+
+ public void setHolderName(String holderName) {
+ ((SwishAccountContractData) contractData).setHolderName(holderName);
+ }
+
+ public String getHolderName() {
+ return ((SwishAccountContractData) contractData).getHolderName();
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/payment/SwishAccountContractData.java b/core/src/main/java/io/bitsquare/payment/SwishAccountContractData.java
new file mode 100644
index 0000000000..1dacb37de0
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/payment/SwishAccountContractData.java
@@ -0,0 +1,56 @@
+/*
+ * 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.payment;
+
+import io.bitsquare.app.Version;
+
+import java.io.Serializable;
+
+public class SwishAccountContractData extends PaymentAccountContractData implements Serializable {
+ // That object is sent over the wire, so we need to take care of version compatibility.
+ private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
+
+ private String mobileNr;
+ private String holderName;
+
+ public SwishAccountContractData(String paymentMethod, String id, int maxTradePeriod) {
+ super(paymentMethod, id, maxTradePeriod);
+ }
+
+ public void setMobileNr(String mobileNr) {
+ this.mobileNr = mobileNr;
+ }
+
+ public String getMobileNr() {
+ return mobileNr;
+ }
+
+ public String getHolderName() {
+ return holderName;
+ }
+
+ public void setHolderName(String holderName) {
+ this.holderName = holderName;
+ }
+
+ @Override
+ public String getPaymentDetails() {
+ return "Swish - Holder name: " + holderName + ", mobile nr.: " + mobileNr;
+ }
+
+}
diff --git a/core/src/main/java/io/bitsquare/payment/unused/FedWireAccount.java b/core/src/main/java/io/bitsquare/payment/unused/FedWireAccount.java
new file mode 100644
index 0000000000..b0fe5ff9a1
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/payment/unused/FedWireAccount.java
@@ -0,0 +1,97 @@
+/*
+ * 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.payment.unused;
+
+import io.bitsquare.app.Version;
+import io.bitsquare.payment.PaymentAccount;
+import io.bitsquare.payment.PaymentMethod;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.Serializable;
+
+// US only
+public class FedWireAccount extends PaymentAccount implements Serializable {
+ // That object is saved to disc. We need to take care of changes to not break deserialization.
+ private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
+
+ private static final Logger log = LoggerFactory.getLogger(FedWireAccount.class);
+
+ private String holderName;
+ private String holderState;
+ private String holderZIP;
+ private String holderStreet;
+ private String holderCity;
+ private String holderSSN; // Optional? social security Nr only Arizona and Oklahoma?
+ private String accountNr;
+ private String bankCode;// SWIFT Code/BIC/RoutingNr/ABA (ABA for UD domestic)
+ private String bankName;
+ private String bankState;
+ private String bankZIP;
+ private String bankStreet;
+ private String bankCity;
+
+ private FedWireAccount() {
+ super(PaymentMethod.SEPA);
+ }
+
+ public String getHolderName() {
+ return holderName;
+ }
+
+ public void setHolderName(String holderName) {
+ this.holderName = holderName;
+ }
+
+ public String getAccountNr() {
+ return accountNr;
+ }
+
+ public void setAccountNr(String accountNr) {
+ this.accountNr = accountNr;
+ }
+
+ public String getBankCode() {
+ return bankCode;
+ }
+
+ public void setBankCode(String bankCode) {
+ this.bankCode = bankCode;
+ }
+
+ @Override
+ public String getPaymentDetails() {
+ return "{accountName='" + accountName + '\'' +
+ '}';
+ }
+
+ @Override
+ public String toString() {
+ return "SepaAccount{" +
+ "accountName='" + accountName + '\'' +
+ ", id='" + id + '\'' +
+ ", paymentMethod=" + paymentMethod +
+ ", holderName='" + holderName + '\'' +
+ ", accountNr='" + accountNr + '\'' +
+ ", bankCode='" + bankCode + '\'' +
+ ", country=" + country +
+ ", tradeCurrencies='" + getTradeCurrencies() + '\'' +
+ ", selectedTradeCurrency=" + selectedTradeCurrency +
+ '}';
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/payment/unused/TransferWiseAccount.java b/core/src/main/java/io/bitsquare/payment/unused/TransferWiseAccount.java
new file mode 100644
index 0000000000..63cd839a78
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/payment/unused/TransferWiseAccount.java
@@ -0,0 +1,87 @@
+/*
+ * 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.payment.unused;
+
+import io.bitsquare.app.Version;
+import io.bitsquare.payment.PaymentAccount;
+import io.bitsquare.payment.PaymentMethod;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.Serializable;
+
+public class TransferWiseAccount extends PaymentAccount implements Serializable {
+ // That object is saved to disc. We need to take care of changes to not break deserialization.
+ private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
+
+ private static final Logger log = LoggerFactory.getLogger(TransferWiseAccount.class);
+
+ private String holderName;
+ private String iban;
+ private String bic;
+
+
+ private TransferWiseAccount() {
+ super(PaymentMethod.SEPA);
+ }
+
+ public String getHolderName() {
+ return holderName;
+ }
+
+ public void setHolderName(String holderName) {
+ this.holderName = holderName;
+ }
+
+ public String getIban() {
+ return iban;
+ }
+
+ public void setIban(String iban) {
+ this.iban = iban;
+ }
+
+ public String getBic() {
+ return bic;
+ }
+
+ public void setBic(String bic) {
+ this.bic = bic;
+ }
+
+ @Override
+ public String getPaymentDetails() {
+ return "TransferWise{accountName='" + accountName + '\'' +
+ '}';
+ }
+
+ @Override
+ public String toString() {
+ return "TransferWiseAccount{" +
+ "accountName='" + accountName + '\'' +
+ ", id='" + id + '\'' +
+ ", paymentMethod=" + paymentMethod +
+ ", holderName='" + holderName + '\'' +
+ ", iban='" + iban + '\'' +
+ ", bic='" + bic + '\'' +
+ ", country=" + country +
+ ", tradeCurrencies='" + getTradeCurrencies() + '\'' +
+ ", selectedTradeCurrency=" + selectedTradeCurrency +
+ '}';
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/payment/unused/USPostalMoneyOrderAccount.java b/core/src/main/java/io/bitsquare/payment/unused/USPostalMoneyOrderAccount.java
new file mode 100644
index 0000000000..83845d26f8
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/payment/unused/USPostalMoneyOrderAccount.java
@@ -0,0 +1,86 @@
+/*
+ * 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.payment.unused;
+
+import io.bitsquare.app.Version;
+import io.bitsquare.payment.PaymentAccount;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.Serializable;
+
+public class USPostalMoneyOrderAccount extends PaymentAccount implements Serializable {
+ // That object is saved to disc. We need to take care of changes to not break deserialization.
+ private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
+
+ private static final Logger log = LoggerFactory.getLogger(USPostalMoneyOrderAccount.class);
+
+ private String holderName;
+ private String iban;
+ private String bic;
+
+
+ private USPostalMoneyOrderAccount() {
+ super(null /*PaymentMethod.US_POSTAL_MONEY_ORDER*/);
+ }
+
+ public String getHolderName() {
+ return holderName;
+ }
+
+ public void setHolderName(String holderName) {
+ this.holderName = holderName;
+ }
+
+ public String getIban() {
+ return iban;
+ }
+
+ public void setIban(String iban) {
+ this.iban = iban;
+ }
+
+ public String getBic() {
+ return bic;
+ }
+
+ public void setBic(String bic) {
+ this.bic = bic;
+ }
+
+ @Override
+ public String getPaymentDetails() {
+ return "{accountName='" + accountName + '\'' +
+ '}';
+ }
+
+ @Override
+ public String toString() {
+ return "USPostalMoneyOrderAccount{" +
+ "accountName='" + accountName + '\'' +
+ ", id='" + id + '\'' +
+ ", paymentMethod=" + paymentMethod +
+ ", holderName='" + holderName + '\'' +
+ ", iban='" + iban + '\'' +
+ ", bic='" + bic + '\'' +
+ ", country=" + country +
+ ", tradeCurrencies='" + getTradeCurrencies() + '\'' +
+ ", selectedTradeCurrency=" + selectedTradeCurrency +
+ '}';
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/trade/handlers/TradeResultHandler.java b/core/src/main/java/io/bitsquare/trade/handlers/TradeResultHandler.java
new file mode 100644
index 0000000000..96348fabda
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/trade/handlers/TradeResultHandler.java
@@ -0,0 +1,25 @@
+/*
+ * 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.trade.handlers;
+
+import io.bitsquare.trade.Trade;
+
+
+public interface TradeResultHandler {
+ void handleResult(Trade trade);
+}
diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/ArbitrationSelectionRule.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/ArbitrationSelectionRule.java
new file mode 100644
index 0000000000..78829bdf20
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/ArbitrationSelectionRule.java
@@ -0,0 +1,47 @@
+/*
+ * 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.trade.protocol.trade;
+
+import io.bitsquare.p2p.Address;
+import io.bitsquare.trade.offer.Offer;
+import org.bitcoinj.core.Sha256Hash;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+public class ArbitrationSelectionRule {
+ private static final Logger log = LoggerFactory.getLogger(ArbitrationSelectionRule.class);
+
+ public static Address select(List acceptedArbitratorAddresses, Offer offer) {
+ List candidates = new ArrayList<>();
+ for (Address offerArbitratorAddress : offer.getArbitratorAddresses()) {
+ candidates.addAll(acceptedArbitratorAddresses.stream().filter(offerArbitratorAddress::equals).collect(Collectors.toList()));
+ }
+ checkArgument(candidates.size() > 0, "candidates.size() <= 0");
+
+ int index = Math.abs(Sha256Hash.hash(offer.getId().getBytes()).hashCode()) % candidates.size();
+ Address selectedArbitrator = candidates.get(index);
+ log.debug("selectedArbitrator " + selectedArbitrator);
+ return selectedArbitrator;
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/CreateAndSignDepositTxAsBuyer.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/CreateAndSignDepositTxAsBuyer.java
new file mode 100644
index 0000000000..c42e31d117
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/CreateAndSignDepositTxAsBuyer.java
@@ -0,0 +1,75 @@
+/*
+ * 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.trade.protocol.trade.tasks.buyer;
+
+import io.bitsquare.btc.FeePolicy;
+import io.bitsquare.btc.data.PreparedDepositTxAndOffererInputs;
+import io.bitsquare.common.crypto.CryptoUtil;
+import io.bitsquare.common.taskrunner.TaskRunner;
+import io.bitsquare.trade.Trade;
+import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
+import org.bitcoinj.core.Coin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class CreateAndSignDepositTxAsBuyer extends TradeTask {
+ private static final Logger log = LoggerFactory.getLogger(CreateAndSignDepositTxAsBuyer.class);
+
+ public CreateAndSignDepositTxAsBuyer(TaskRunner taskHandler, Trade trade) {
+ super(taskHandler, trade);
+ }
+
+ @Override
+ protected void run() {
+ try {
+ runInterceptHook();
+ checkNotNull(trade.getTradeAmount(), "trade.getTradeAmount() must not be null");
+ Coin buyerInputAmount = FeePolicy.SECURITY_DEPOSIT.add(FeePolicy.TX_FEE);
+ Coin msOutputAmount = buyerInputAmount.add(FeePolicy.SECURITY_DEPOSIT).add(trade.getTradeAmount());
+
+ log.debug("getContractAsJson");
+ log.debug("----------");
+ log.debug(trade.getContractAsJson());
+ log.debug("----------");
+
+ byte[] contractHash = CryptoUtil.getHash(trade.getContractAsJson());
+ trade.setContractHash(contractHash);
+ PreparedDepositTxAndOffererInputs result = processModel.getTradeWalletService().offererCreatesAndSignsDepositTx(
+ true,
+ contractHash,
+ buyerInputAmount,
+ msOutputAmount,
+ processModel.tradingPeer.getRawInputs(),
+ processModel.tradingPeer.getChangeOutputValue(),
+ processModel.tradingPeer.getChangeOutputAddress(),
+ processModel.getAddressEntry(),
+ processModel.getTradeWalletPubKey(),
+ processModel.tradingPeer.getTradeWalletPubKey(),
+ processModel.getArbitratorPubKey(trade.getArbitratorAddress()));
+
+ processModel.setPreparedDepositTx(result.depositTransaction);
+ processModel.setRawInputs(result.rawOffererInputs);
+
+ complete();
+ } catch (Throwable t) {
+ failed(t);
+ }
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/CreateDepositTxInputsAsBuyer.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/CreateDepositTxInputsAsBuyer.java
new file mode 100644
index 0000000000..23b9821085
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/CreateDepositTxInputsAsBuyer.java
@@ -0,0 +1,51 @@
+/*
+ * 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.trade.protocol.trade.tasks.buyer;
+
+import io.bitsquare.btc.FeePolicy;
+import io.bitsquare.btc.data.InputsAndChangeOutput;
+import io.bitsquare.common.taskrunner.TaskRunner;
+import io.bitsquare.trade.Trade;
+import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
+import org.bitcoinj.core.Coin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CreateDepositTxInputsAsBuyer extends TradeTask {
+ private static final Logger log = LoggerFactory.getLogger(CreateDepositTxInputsAsBuyer.class);
+
+ public CreateDepositTxInputsAsBuyer(TaskRunner taskHandler, Trade trade) {
+ super(taskHandler, trade);
+ }
+
+ @Override
+ protected void run() {
+ try {
+ runInterceptHook();
+ Coin takerInputAmount = FeePolicy.SECURITY_DEPOSIT.add(FeePolicy.TX_FEE);
+ InputsAndChangeOutput result = processModel.getTradeWalletService().takerCreatesDepositsTxInputs(takerInputAmount, processModel.getAddressEntry());
+ processModel.setRawInputs(result.rawInputs);
+ processModel.setChangeOutputValue(result.changeOutputValue);
+ processModel.setChangeOutputAddress(result.changeOutputAddress);
+
+ complete();
+ } catch (Throwable t) {
+ failed(t);
+ }
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SignAndPublishDepositTxAsBuyer.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SignAndPublishDepositTxAsBuyer.java
new file mode 100644
index 0000000000..12329e3647
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SignAndPublishDepositTxAsBuyer.java
@@ -0,0 +1,76 @@
+/*
+ * 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.trade.protocol.trade.tasks.buyer;
+
+import com.google.common.util.concurrent.FutureCallback;
+import io.bitsquare.common.crypto.CryptoUtil;
+import io.bitsquare.common.taskrunner.TaskRunner;
+import io.bitsquare.trade.Trade;
+import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
+import org.bitcoinj.core.Transaction;
+import org.jetbrains.annotations.NotNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Date;
+
+public class SignAndPublishDepositTxAsBuyer extends TradeTask {
+ private static final Logger log = LoggerFactory.getLogger(SignAndPublishDepositTxAsBuyer.class);
+
+ public SignAndPublishDepositTxAsBuyer(TaskRunner taskHandler, Trade trade) {
+ super(taskHandler, trade);
+ }
+
+ @Override
+ protected void run() {
+ try {
+ runInterceptHook();
+
+ byte[] contractHash = CryptoUtil.getHash(trade.getContractAsJson());
+ trade.setContractHash(contractHash);
+ processModel.getTradeWalletService().takerSignsAndPublishesDepositTx(
+ false,
+ contractHash,
+ processModel.getPreparedDepositTx(),
+ processModel.getRawInputs(),
+ processModel.tradingPeer.getRawInputs(),
+ processModel.getTradeWalletPubKey(),
+ processModel.tradingPeer.getTradeWalletPubKey(),
+ processModel.getArbitratorPubKey(trade.getArbitratorAddress()),
+ new FutureCallback() {
+ @Override
+ public void onSuccess(Transaction transaction) {
+ log.trace("takerSignAndPublishTx succeeded " + transaction);
+
+ trade.setDepositTx(transaction);
+ trade.setTakeOfferDate(new Date());
+ trade.setState(Trade.State.DEPOSIT_PUBLISHED);
+
+ complete();
+ }
+
+ @Override
+ public void onFailure(@NotNull Throwable t) {
+ failed(t);
+ }
+ });
+ } catch (Throwable t) {
+ failed(t);
+ }
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/AddDepositTxToWallet.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/AddDepositTxToWallet.java
new file mode 100644
index 0000000000..f370021985
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/AddDepositTxToWallet.java
@@ -0,0 +1,48 @@
+/*
+ * 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.trade.protocol.trade.tasks.offerer;
+
+import io.bitsquare.common.taskrunner.TaskRunner;
+import io.bitsquare.trade.Trade;
+import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
+import org.bitcoinj.core.Transaction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AddDepositTxToWallet extends TradeTask {
+ private static final Logger log = LoggerFactory.getLogger(AddDepositTxToWallet.class);
+
+ public AddDepositTxToWallet(TaskRunner taskHandler, Trade trade) {
+ super(taskHandler, trade);
+ }
+
+ @Override
+ protected void run() {
+ try {
+ runInterceptHook();
+ // To access tx confidence we need to add that tx into our wallet.
+ Transaction depositTx = processModel.getTradeWalletService().addTransactionToWallet(trade.getDepositTx());
+ // update with full tx
+ trade.setDepositTx(depositTx);
+
+ complete();
+ } catch (Throwable t) {
+ failed(t);
+ }
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/CreateAndSignContract.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/CreateAndSignContract.java
new file mode 100644
index 0000000000..a26fa33757
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/CreateAndSignContract.java
@@ -0,0 +1,89 @@
+/*
+ * 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.trade.protocol.trade.tasks.offerer;
+
+import io.bitsquare.common.crypto.CryptoUtil;
+import io.bitsquare.common.taskrunner.TaskRunner;
+import io.bitsquare.common.util.Utilities;
+import io.bitsquare.p2p.Address;
+import io.bitsquare.payment.PaymentAccountContractData;
+import io.bitsquare.trade.BuyerAsOffererTrade;
+import io.bitsquare.trade.Contract;
+import io.bitsquare.trade.Trade;
+import io.bitsquare.trade.protocol.trade.TradingPeer;
+import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class CreateAndSignContract extends TradeTask {
+ private static final Logger log = LoggerFactory.getLogger(CreateAndSignContract.class);
+
+ public CreateAndSignContract(TaskRunner taskHandler, Trade trade) {
+ super(taskHandler, trade);
+ }
+
+ @Override
+ protected void run() {
+ try {
+ runInterceptHook();
+ checkNotNull(processModel.getTakeOfferFeeTxId(), "processModel.getTakeOfferFeeTxId() must not be null");
+
+ TradingPeer taker = processModel.tradingPeer;
+ PaymentAccountContractData offererPaymentAccountContractData = processModel.getPaymentAccountContractData(trade);
+ PaymentAccountContractData takerPaymentAccountContractData = taker.getPaymentAccountContractData();
+ boolean isBuyerOffererOrSellerTaker = trade instanceof BuyerAsOffererTrade;
+
+ Address buyerAddress = isBuyerOffererOrSellerTaker ? processModel.getMyAddress() : processModel.getTempTradingPeerAddress();
+ Address sellerAddress = isBuyerOffererOrSellerTaker ? processModel.getTempTradingPeerAddress() : processModel.getMyAddress();
+ log.debug("isBuyerOffererOrSellerTaker " + isBuyerOffererOrSellerTaker);
+ log.debug("buyerAddress " + buyerAddress);
+ log.debug("sellerAddress " + sellerAddress);
+ Contract contract = new Contract(
+ processModel.getOffer(),
+ trade.getTradeAmount(),
+ processModel.getTakeOfferFeeTxId(),
+ buyerAddress,
+ sellerAddress,
+ trade.getArbitratorAddress(),
+ isBuyerOffererOrSellerTaker,
+ processModel.getAccountId(),
+ taker.getAccountId(),
+ offererPaymentAccountContractData,
+ takerPaymentAccountContractData,
+ processModel.getPubKeyRing(),
+ taker.getPubKeyRing(),
+ processModel.getAddressEntry().getAddressString(),
+ taker.getPayoutAddressString(),
+ processModel.getTradeWalletPubKey(),
+ taker.getTradeWalletPubKey()
+ );
+ String contractAsJson = Utilities.objectToJson(contract);
+ String signature = CryptoUtil.signMessage(processModel.getKeyRing().getMsgSignatureKeyPair().getPrivate(), contractAsJson);
+
+ trade.setContract(contract);
+ trade.setContractAsJson(contractAsJson);
+ trade.setOffererContractSignature(signature);
+
+ complete();
+ } catch (Throwable t) {
+ failed(t);
+ }
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/LoadTakeOfferFeeTx.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/LoadTakeOfferFeeTx.java
new file mode 100644
index 0000000000..8bb84229de
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/LoadTakeOfferFeeTx.java
@@ -0,0 +1,46 @@
+/*
+ * 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.trade.protocol.trade.tasks.offerer;
+
+import io.bitsquare.common.taskrunner.TaskRunner;
+import io.bitsquare.trade.Trade;
+import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class LoadTakeOfferFeeTx extends TradeTask {
+ private static final Logger log = LoggerFactory.getLogger(LoadTakeOfferFeeTx.class);
+
+ public LoadTakeOfferFeeTx(TaskRunner taskHandler, Trade trade) {
+ super(taskHandler, trade);
+ }
+
+ @Override
+ protected void run() {
+ try {
+ runInterceptHook();
+
+ // TODO impl. not completed
+ //processModel.getWalletService().findTxInBlockChain(processModel.getTakeOfferFeeTxId());
+
+ complete();
+ } catch (Throwable t) {
+ failed(t);
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessDepositTxPublishedMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessDepositTxPublishedMessage.java
new file mode 100644
index 0000000000..2624041334
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessDepositTxPublishedMessage.java
@@ -0,0 +1,65 @@
+/*
+ * 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.trade.protocol.trade.tasks.offerer;
+
+import io.bitsquare.common.taskrunner.TaskRunner;
+import io.bitsquare.trade.OffererTrade;
+import io.bitsquare.trade.Trade;
+import io.bitsquare.trade.protocol.trade.messages.DepositTxPublishedMessage;
+import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Date;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static io.bitsquare.util.Validator.checkTradeId;
+
+public class ProcessDepositTxPublishedMessage extends TradeTask {
+ private static final Logger log = LoggerFactory.getLogger(ProcessDepositTxPublishedMessage.class);
+
+ public ProcessDepositTxPublishedMessage(TaskRunner taskHandler, Trade trade) {
+ super(taskHandler, trade);
+ }
+
+ @Override
+ protected void run() {
+ try {
+ runInterceptHook();
+ log.debug("current trade state " + trade.getState());
+ DepositTxPublishedMessage message = (DepositTxPublishedMessage) processModel.getTradeMessage();
+ checkTradeId(processModel.getId(), message);
+ checkNotNull(message);
+ checkArgument(message.depositTx != null);
+ trade.setDepositTx(processModel.getWalletService().getTransactionFromSerializedTx(message.depositTx));
+ trade.setState(Trade.State.DEPOSIT_PUBLISHED_MSG_RECEIVED);
+ trade.setTakeOfferDate(new Date());
+
+ if (trade instanceof OffererTrade)
+ processModel.getOpenOfferManager().closeOpenOffer(trade.getOffer());
+
+ // update to the latest peer address of our peer if the message is correct
+ trade.setTradingPeerAddress(processModel.getTempTradingPeerAddress());
+
+ complete();
+ } catch (Throwable t) {
+ failed(t);
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessPayDepositRequest.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessPayDepositRequest.java
new file mode 100644
index 0000000000..41e2389fd9
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessPayDepositRequest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.trade.protocol.trade.tasks.offerer;
+
+import io.bitsquare.common.crypto.CryptoUtil;
+import io.bitsquare.common.taskrunner.TaskRunner;
+import io.bitsquare.locale.CurrencyUtil;
+import io.bitsquare.payment.BlockChainAccountContractData;
+import io.bitsquare.payment.PaymentAccountContractData;
+import io.bitsquare.trade.Trade;
+import io.bitsquare.trade.protocol.trade.messages.PayDepositRequest;
+import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
+import org.bitcoinj.core.Coin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static io.bitsquare.util.Validator.checkTradeId;
+import static io.bitsquare.util.Validator.nonEmptyStringOf;
+
+public class ProcessPayDepositRequest extends TradeTask {
+ private static final Logger log = LoggerFactory.getLogger(ProcessPayDepositRequest.class);
+
+ public ProcessPayDepositRequest(TaskRunner taskHandler, Trade trade) {
+ super(taskHandler, trade);
+ }
+
+ @Override
+ protected void run() {
+ try {
+ runInterceptHook();
+ log.debug("current trade state " + trade.getState());
+ PayDepositRequest payDepositRequest = (PayDepositRequest) processModel.getTradeMessage();
+ checkTradeId(processModel.getId(), payDepositRequest);
+ checkNotNull(payDepositRequest);
+
+ processModel.tradingPeer.setRawInputs(checkNotNull(payDepositRequest.rawInputs));
+ checkArgument(payDepositRequest.rawInputs.size() > 0);
+
+ processModel.tradingPeer.setChangeOutputValue(payDepositRequest.changeOutputValue);
+ if (payDepositRequest.changeOutputAddress != null)
+ processModel.tradingPeer.setChangeOutputAddress(payDepositRequest.changeOutputAddress);
+
+ processModel.tradingPeer.setTradeWalletPubKey(checkNotNull(payDepositRequest.takerTradeWalletPubKey));
+ processModel.tradingPeer.setPayoutAddressString(nonEmptyStringOf(payDepositRequest.takerPayoutAddressString));
+ processModel.tradingPeer.setPubKeyRing(checkNotNull(payDepositRequest.takerPubKeyRing));
+
+ PaymentAccountContractData paymentAccountContractData = checkNotNull(payDepositRequest.takerPaymentAccountContractData);
+ processModel.tradingPeer.setPaymentAccountContractData(paymentAccountContractData);
+ // We apply the payment ID in case its a cryptoNote coin. It is created form the hash of the trade ID
+ if (paymentAccountContractData instanceof BlockChainAccountContractData &&
+ CurrencyUtil.isCryptoNoteCoin(processModel.getOffer().getCurrencyCode())) {
+ String paymentId = CryptoUtil.getHashAsHex(trade.getId()).substring(0, 32);
+ ((BlockChainAccountContractData) paymentAccountContractData).setPaymentId(paymentId);
+ }
+
+ processModel.tradingPeer.setAccountId(nonEmptyStringOf(payDepositRequest.takerAccountId));
+ processModel.setTakeOfferFeeTxId(nonEmptyStringOf(payDepositRequest.takeOfferFeeTxId));
+ processModel.setTakerAcceptedArbitratorAddresses(checkNotNull(payDepositRequest.acceptedArbitratorAddresses));
+ if (payDepositRequest.acceptedArbitratorAddresses.size() < 1)
+ failed("acceptedArbitratorNames size must be at least 1");
+ trade.setArbitratorAddress(checkNotNull(payDepositRequest.arbitratorAddress));
+ checkArgument(payDepositRequest.tradeAmount > 0);
+ trade.setTradeAmount(Coin.valueOf(payDepositRequest.tradeAmount));
+
+ // update to the latest peer address of our peer if the payDepositRequest is correct
+ trade.setTradingPeerAddress(processModel.getTempTradingPeerAddress());
+
+ complete();
+ } catch (Throwable t) {
+ failed(t);
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/SendPublishDepositTxRequest.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/SendPublishDepositTxRequest.java
new file mode 100644
index 0000000000..db06161dce
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/SendPublishDepositTxRequest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.trade.protocol.trade.tasks.offerer;
+
+import io.bitsquare.common.taskrunner.TaskRunner;
+import io.bitsquare.p2p.messaging.SendMailMessageListener;
+import io.bitsquare.trade.Trade;
+import io.bitsquare.trade.protocol.trade.messages.PublishDepositTxRequest;
+import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SendPublishDepositTxRequest extends TradeTask {
+ private static final Logger log = LoggerFactory.getLogger(SendPublishDepositTxRequest.class);
+
+ public SendPublishDepositTxRequest(TaskRunner taskHandler, Trade trade) {
+ super(taskHandler, trade);
+ }
+
+ @Override
+ protected void run() {
+ try {
+ runInterceptHook();
+ trade.setState(Trade.State.DEPOSIT_PUBLISH_REQUESTED);
+ PublishDepositTxRequest tradeMessage = new PublishDepositTxRequest(
+ processModel.getId(),
+ processModel.getPaymentAccountContractData(trade),
+ processModel.getAccountId(),
+ processModel.getTradeWalletPubKey(),
+ trade.getContractAsJson(),
+ trade.getOffererContractSignature(),
+ processModel.getAddressEntry().getAddressString(),
+ processModel.getPreparedDepositTx(),
+ processModel.getRawInputs(),
+ trade.getOpenDisputeTimeAsBlockHeight(),
+ trade.getCheckPaymentTimeAsBlockHeight()
+ );
+
+ processModel.getP2PService().sendEncryptedMailMessage(
+ trade.getTradingPeerAddress(),
+ processModel.tradingPeer.getPubKeyRing(),
+ tradeMessage,
+ new SendMailMessageListener() {
+ @Override
+ public void onArrived() {
+ log.trace("Message arrived at peer.");
+ complete();
+ }
+
+ @Override
+ public void onFault() {
+ appendToErrorMessage("PublishDepositTxRequest sending failed");
+ failed();
+ }
+ }
+ );
+ } catch (Throwable t) {
+ failed(t);
+ }
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/SetupDepositBalanceListener.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/SetupDepositBalanceListener.java
new file mode 100644
index 0000000000..61085f92be
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/SetupDepositBalanceListener.java
@@ -0,0 +1,108 @@
+/*
+ * 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.trade.protocol.trade.tasks.offerer;
+
+import io.bitsquare.btc.WalletService;
+import io.bitsquare.btc.listeners.BalanceListener;
+import io.bitsquare.common.UserThread;
+import io.bitsquare.common.taskrunner.TaskRunner;
+import io.bitsquare.trade.OffererTrade;
+import io.bitsquare.trade.Trade;
+import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
+import org.bitcoinj.core.Address;
+import org.bitcoinj.core.Coin;
+import org.fxmisc.easybind.EasyBind;
+import org.fxmisc.easybind.Subscription;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+// The buyer waits for the msg from the seller that he has published the deposit tx.
+// In error case he might not get that msg so we check additionally the balance of our inputs, if it is zero, it means the deposit
+// is already published. We set then the DEPOSIT_LOCKED state, so the user get informed that he is already in the critical state and need
+// to request support.
+public class SetupDepositBalanceListener extends TradeTask {
+ private static final Logger log = LoggerFactory.getLogger(SetupDepositBalanceListener.class);
+ private Subscription tradeStateSubscription;
+ private BalanceListener balanceListener;
+
+ public SetupDepositBalanceListener(TaskRunner taskHandler, Trade trade) {
+ super(taskHandler, trade);
+ }
+
+ @Override
+ protected void run() {
+ try {
+ runInterceptHook();
+
+ WalletService walletService = processModel.getWalletService();
+ Address address = walletService.getAddressEntryByOfferId(trade.getId()).getAddress();
+ balanceListener = walletService.addBalanceListener(new BalanceListener(address) {
+ @Override
+ public void onBalanceChanged(Coin balance) {
+ updateBalance(balance);
+ }
+ });
+ walletService.addBalanceListener(balanceListener);
+
+ tradeStateSubscription = EasyBind.subscribe(trade.stateProperty(), newValue -> {
+ log.debug("tradeStateSubscription newValue " + newValue);
+ if (newValue == Trade.State.DEPOSIT_PUBLISHED_MSG_RECEIVED
+ || newValue == Trade.State.DEPOSIT_SEEN_IN_NETWORK) {
+
+ walletService.removeBalanceListener(balanceListener);
+ log.debug(" UserThread.execute(this::unSubscribe);");
+ // TODO is that allowed?
+ UserThread.execute(this::unSubscribe);
+ }
+ });
+ updateBalance(walletService.getBalanceForAddress(address));
+
+ // we complete immediately, our object stays alive because the balanceListener is stored in the WalletService
+ complete();
+ } catch (Throwable t) {
+ failed(t);
+ }
+ }
+
+ private void unSubscribe() {
+ //TODO investigate, seems to not get called sometimes
+ log.debug("unSubscribe tradeStateSubscription");
+ tradeStateSubscription.unsubscribe();
+ }
+
+ private void updateBalance(Coin balance) {
+ log.debug("updateBalance " + balance.toFriendlyString());
+ log.debug("pre tradeState " + trade.getState().toString());
+ Trade.State tradeState = trade.getState();
+ if (balance.compareTo(Coin.ZERO) == 0) {
+ if (trade instanceof OffererTrade) {
+ processModel.getOpenOfferManager().closeOpenOffer(trade.getOffer());
+
+ if (tradeState == Trade.State.DEPOSIT_PUBLISH_REQUESTED) {
+ trade.setState(Trade.State.DEPOSIT_SEEN_IN_NETWORK);
+ } else if (tradeState.getPhase() == Trade.Phase.PREPARATION) {
+ processModel.getTradeManager().removePreparedTrade(trade);
+ } else if (tradeState.getPhase().ordinal() < Trade.Phase.DEPOSIT_PAID.ordinal()) {
+ processModel.getTradeManager().addTradeToFailedTrades(trade);
+ }
+ }
+ }
+
+ log.debug("tradeState " + trade.getState().toString());
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/VerifyArbitrationSelection.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/VerifyArbitrationSelection.java
new file mode 100644
index 0000000000..bd147ba405
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/VerifyArbitrationSelection.java
@@ -0,0 +1,48 @@
+/*
+ * 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.trade.protocol.trade.tasks.offerer;
+
+import io.bitsquare.common.taskrunner.TaskRunner;
+import io.bitsquare.trade.Trade;
+import io.bitsquare.trade.protocol.trade.ArbitrationSelectionRule;
+import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class VerifyArbitrationSelection extends TradeTask {
+ private static final Logger log = LoggerFactory.getLogger(VerifyArbitrationSelection.class);
+
+ public VerifyArbitrationSelection(TaskRunner taskHandler, Trade trade) {
+ super(taskHandler, trade);
+ }
+
+ @Override
+ protected void run() {
+ try {
+ runInterceptHook();
+
+ if (trade.getArbitratorAddress().equals(ArbitrationSelectionRule.select(processModel.getTakerAcceptedArbitratorAddresses(),
+ processModel.getOffer())))
+ complete();
+ else
+ failed("Arbitrator selection verification failed");
+ } catch (Throwable t) {
+ failed(t);
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/CreateAndSignDepositTxAsSeller.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/CreateAndSignDepositTxAsSeller.java
new file mode 100644
index 0000000000..957de8ea7e
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/CreateAndSignDepositTxAsSeller.java
@@ -0,0 +1,70 @@
+/*
+ * 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.trade.protocol.trade.tasks.seller;
+
+import io.bitsquare.btc.FeePolicy;
+import io.bitsquare.btc.data.PreparedDepositTxAndOffererInputs;
+import io.bitsquare.common.crypto.CryptoUtil;
+import io.bitsquare.common.taskrunner.TaskRunner;
+import io.bitsquare.trade.Trade;
+import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
+import org.bitcoinj.core.Coin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class CreateAndSignDepositTxAsSeller extends TradeTask {
+ private static final Logger log = LoggerFactory.getLogger(CreateAndSignDepositTxAsSeller.class);
+
+ public CreateAndSignDepositTxAsSeller(TaskRunner taskHandler, Trade trade) {
+ super(taskHandler, trade);
+ }
+
+ @Override
+ protected void run() {
+ try {
+ runInterceptHook();
+ checkNotNull(trade.getTradeAmount(), "trade.getTradeAmount() must not be null");
+ Coin sellerInputAmount = FeePolicy.SECURITY_DEPOSIT.add(FeePolicy.TX_FEE).add(trade.getTradeAmount());
+ Coin msOutputAmount = sellerInputAmount.add(FeePolicy.SECURITY_DEPOSIT);
+
+ byte[] contractHash = CryptoUtil.getHash(trade.getContractAsJson());
+ trade.setContractHash(contractHash);
+ PreparedDepositTxAndOffererInputs result = processModel.getTradeWalletService().offererCreatesAndSignsDepositTx(
+ false,
+ contractHash,
+ sellerInputAmount,
+ msOutputAmount,
+ processModel.tradingPeer.getRawInputs(),
+ processModel.tradingPeer.getChangeOutputValue(),
+ processModel.tradingPeer.getChangeOutputAddress(),
+ processModel.getAddressEntry(),
+ processModel.tradingPeer.getTradeWalletPubKey(),
+ processModel.getTradeWalletPubKey(),
+ processModel.getArbitratorPubKey(trade.getArbitratorAddress()));
+
+ processModel.setPreparedDepositTx(result.depositTransaction);
+ processModel.setRawInputs(result.rawOffererInputs);
+
+ complete();
+ } catch (Throwable t) {
+ failed(t);
+ }
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/CreateDepositTxInputsAsSeller.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/CreateDepositTxInputsAsSeller.java
new file mode 100644
index 0000000000..f523595c97
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/CreateDepositTxInputsAsSeller.java
@@ -0,0 +1,57 @@
+/*
+ * 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.trade.protocol.trade.tasks.seller;
+
+import io.bitsquare.btc.FeePolicy;
+import io.bitsquare.btc.data.InputsAndChangeOutput;
+import io.bitsquare.common.taskrunner.TaskRunner;
+import io.bitsquare.trade.Trade;
+import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
+import org.bitcoinj.core.Coin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CreateDepositTxInputsAsSeller extends TradeTask {
+ private static final Logger log = LoggerFactory.getLogger(CreateDepositTxInputsAsSeller.class);
+
+ public CreateDepositTxInputsAsSeller(TaskRunner taskHandler, Trade trade) {
+ super(taskHandler, trade);
+ }
+
+ @Override
+ protected void run() {
+ try {
+ runInterceptHook();
+ if (trade.getTradeAmount() != null) {
+ Coin takerInputAmount = FeePolicy.SECURITY_DEPOSIT.add(FeePolicy.TX_FEE).add(trade.getTradeAmount());
+
+ InputsAndChangeOutput result = processModel.getTradeWalletService().takerCreatesDepositsTxInputs(takerInputAmount, processModel
+ .getAddressEntry());
+ processModel.setRawInputs(result.rawInputs);
+ processModel.setChangeOutputValue(result.changeOutputValue);
+ processModel.setChangeOutputAddress(result.changeOutputAddress);
+
+ complete();
+ } else {
+ failed("trade.getTradeAmount() = null");
+ }
+ } catch (Throwable t) {
+ failed(t);
+ }
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SignAndPublishDepositTxAsSeller.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SignAndPublishDepositTxAsSeller.java
new file mode 100644
index 0000000000..062279d1c9
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SignAndPublishDepositTxAsSeller.java
@@ -0,0 +1,79 @@
+/*
+ * 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.trade.protocol.trade.tasks.seller;
+
+import com.google.common.util.concurrent.FutureCallback;
+import io.bitsquare.common.crypto.CryptoUtil;
+import io.bitsquare.common.taskrunner.TaskRunner;
+import io.bitsquare.trade.Trade;
+import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
+import org.bitcoinj.core.Transaction;
+import org.jetbrains.annotations.NotNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Date;
+
+public class SignAndPublishDepositTxAsSeller extends TradeTask {
+ private static final Logger log = LoggerFactory.getLogger(SignAndPublishDepositTxAsSeller.class);
+
+ public SignAndPublishDepositTxAsSeller(TaskRunner taskHandler, Trade trade) {
+ super(taskHandler, trade);
+ }
+
+ @Override
+ protected void run() {
+ try {
+ runInterceptHook();
+ log.debug("getContractAsJson");
+ log.debug("----------");
+ log.debug(trade.getContractAsJson());
+ log.debug("----------");
+ byte[] contractHash = CryptoUtil.getHash(trade.getContractAsJson());
+ trade.setContractHash(contractHash);
+ processModel.getTradeWalletService().takerSignsAndPublishesDepositTx(
+ true,
+ contractHash,
+ processModel.getPreparedDepositTx(),
+ processModel.tradingPeer.getRawInputs(),
+ processModel.getRawInputs(),
+ processModel.tradingPeer.getTradeWalletPubKey(),
+ processModel.getTradeWalletPubKey(),
+ processModel.getArbitratorPubKey(trade.getArbitratorAddress()),
+ new FutureCallback() {
+ @Override
+ public void onSuccess(Transaction transaction) {
+ log.trace("takerSignAndPublishTx succeeded " + transaction);
+
+ trade.setDepositTx(transaction);
+ trade.setTakeOfferDate(new Date());
+ trade.setState(Trade.State.DEPOSIT_PUBLISHED);
+
+ complete();
+ }
+
+ @Override
+ public void onFailure(@NotNull Throwable t) {
+ failed(t);
+ }
+ });
+ } catch (Throwable t) {
+ failed(t);
+ }
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/shared/InitWaitPeriodForOpenDispute.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/shared/InitWaitPeriodForOpenDispute.java
new file mode 100644
index 0000000000..15b130118c
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/shared/InitWaitPeriodForOpenDispute.java
@@ -0,0 +1,50 @@
+/*
+ * 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.trade.protocol.trade.tasks.shared;
+
+import io.bitsquare.common.taskrunner.TaskRunner;
+import io.bitsquare.trade.Trade;
+import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class InitWaitPeriodForOpenDispute extends TradeTask {
+ private static final Logger log = LoggerFactory.getLogger(InitWaitPeriodForOpenDispute.class);
+
+ public InitWaitPeriodForOpenDispute(TaskRunner taskHandler, Trade trade) {
+ super(taskHandler, trade);
+ }
+
+ @Override
+ protected void run() {
+ try {
+ runInterceptHook();
+ int openDisputeTimeAsBlockHeight = processModel.getTradeWalletService().getLastBlockSeenHeight()
+ + trade.getOffer().getPaymentMethod().getMaxTradePeriod();
+ trade.setOpenDisputeTimeAsBlockHeight(openDisputeTimeAsBlockHeight);
+
+ int checkPaymentTimeAsBlockHeight = processModel.getTradeWalletService().getLastBlockSeenHeight()
+ + trade.getOffer().getPaymentMethod().getMaxTradePeriod() / 2;
+ trade.setCheckPaymentTimeAsBlockHeight(checkPaymentTimeAsBlockHeight);
+
+ complete();
+ } catch (Throwable t) {
+ failed(t);
+ }
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/shared/SignPayoutTx.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/shared/SignPayoutTx.java
new file mode 100644
index 0000000000..bf283907f4
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/shared/SignPayoutTx.java
@@ -0,0 +1,67 @@
+/*
+ * 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.trade.protocol.trade.tasks.shared;
+
+import io.bitsquare.btc.FeePolicy;
+import io.bitsquare.common.taskrunner.TaskRunner;
+import io.bitsquare.trade.Trade;
+import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
+import org.bitcoinj.core.Coin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class SignPayoutTx extends TradeTask {
+ private static final Logger log = LoggerFactory.getLogger(SignPayoutTx.class);
+
+ public SignPayoutTx(TaskRunner taskHandler, Trade trade) {
+ super(taskHandler, trade);
+ }
+
+ @Override
+ protected void run() {
+ try {
+ runInterceptHook();
+ checkNotNull(trade.getTradeAmount(), "trade.getTradeAmount() must not be null");
+ Coin sellerPayoutAmount = FeePolicy.SECURITY_DEPOSIT;
+ Coin buyerPayoutAmount = sellerPayoutAmount.add(trade.getTradeAmount());
+
+ long lockTimeAsBlockHeight = processModel.getTradeWalletService().getLastBlockSeenHeight() + trade.getOffer().getPaymentMethod().getLockTime();
+ trade.setLockTimeAsBlockHeight(lockTimeAsBlockHeight);
+
+ byte[] payoutTxSignature = processModel.getTradeWalletService().sellerSignsPayoutTx(
+ trade.getDepositTx(),
+ buyerPayoutAmount,
+ sellerPayoutAmount,
+ processModel.tradingPeer.getPayoutAddressString(),
+ processModel.getAddressEntry(),
+ lockTimeAsBlockHeight,
+ processModel.tradingPeer.getTradeWalletPubKey(),
+ processModel.getTradeWalletPubKey(),
+ processModel.getArbitratorPubKey(trade.getArbitratorAddress()));
+
+ processModel.setPayoutTxSignature(payoutTxSignature);
+
+ complete();
+ } catch (Throwable t) {
+ failed(t);
+ }
+ }
+}
+
diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/LoadCreateOfferFeeTx.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/LoadCreateOfferFeeTx.java
new file mode 100644
index 0000000000..e028b8022c
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/LoadCreateOfferFeeTx.java
@@ -0,0 +1,46 @@
+/*
+ * 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.trade.protocol.trade.tasks.taker;
+
+import io.bitsquare.common.taskrunner.TaskRunner;
+import io.bitsquare.trade.Trade;
+import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class LoadCreateOfferFeeTx extends TradeTask {
+ private static final Logger log = LoggerFactory.getLogger(LoadCreateOfferFeeTx.class);
+
+ public LoadCreateOfferFeeTx(TaskRunner taskHandler, Trade trade) {
+ super(taskHandler, trade);
+ }
+
+ @Override
+ protected void run() {
+ try {
+ runInterceptHook();
+
+ // TODO impl. not completed
+ ///processModel.getWalletService().findTxInBlockChain(trade.getOffer().getOfferFeePaymentTxID());
+
+ complete();
+ } catch (Throwable t) {
+ failed(t);
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/ProcessPublishDepositTxRequest.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/ProcessPublishDepositTxRequest.java
new file mode 100644
index 0000000000..214ee743f9
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/ProcessPublishDepositTxRequest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.trade.protocol.trade.tasks.taker;
+
+import io.bitsquare.common.crypto.CryptoUtil;
+import io.bitsquare.common.taskrunner.TaskRunner;
+import io.bitsquare.locale.CurrencyUtil;
+import io.bitsquare.payment.BlockChainAccountContractData;
+import io.bitsquare.payment.PaymentAccountContractData;
+import io.bitsquare.trade.Trade;
+import io.bitsquare.trade.protocol.trade.messages.PublishDepositTxRequest;
+import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static io.bitsquare.util.Validator.checkTradeId;
+import static io.bitsquare.util.Validator.nonEmptyStringOf;
+
+public class ProcessPublishDepositTxRequest extends TradeTask {
+ private static final Logger log = LoggerFactory.getLogger(ProcessPublishDepositTxRequest.class);
+
+ public ProcessPublishDepositTxRequest(TaskRunner taskHandler, Trade trade) {
+ super(taskHandler, trade);
+ }
+
+ @Override
+ protected void run() {
+ try {
+ runInterceptHook();
+ log.debug("current trade state " + trade.getState());
+ PublishDepositTxRequest publishDepositTxRequest = (PublishDepositTxRequest) processModel.getTradeMessage();
+ checkTradeId(processModel.getId(), publishDepositTxRequest);
+ checkNotNull(publishDepositTxRequest);
+
+ PaymentAccountContractData paymentAccountContractData = checkNotNull(publishDepositTxRequest.offererPaymentAccountContractData);
+ processModel.tradingPeer.setPaymentAccountContractData(paymentAccountContractData);
+ // We apply the payment ID in case its a cryptoNote coin. It is created form the hash of the trade ID
+ if (paymentAccountContractData instanceof BlockChainAccountContractData &&
+ CurrencyUtil.isCryptoNoteCoin(processModel.getOffer().getCurrencyCode())) {
+ String paymentId = CryptoUtil.getHashAsHex(trade.getId()).substring(0, 32);
+ ((BlockChainAccountContractData) paymentAccountContractData).setPaymentId(paymentId);
+ }
+
+ processModel.tradingPeer.setAccountId(nonEmptyStringOf(publishDepositTxRequest.offererAccountId));
+ processModel.tradingPeer.setTradeWalletPubKey(checkNotNull(publishDepositTxRequest.offererTradeWalletPubKey));
+ processModel.tradingPeer.setContractAsJson(nonEmptyStringOf(publishDepositTxRequest.offererContractAsJson));
+ processModel.tradingPeer.setContractSignature(nonEmptyStringOf(publishDepositTxRequest.offererContractSignature));
+ processModel.tradingPeer.setPayoutAddressString(nonEmptyStringOf(publishDepositTxRequest.offererPayoutAddressString));
+ processModel.tradingPeer.setRawInputs(checkNotNull(publishDepositTxRequest.offererInputs));
+ processModel.setPreparedDepositTx(checkNotNull(publishDepositTxRequest.preparedDepositTx));
+ checkArgument(publishDepositTxRequest.offererInputs.size() > 0);
+ if (publishDepositTxRequest.openDisputeTimeAsBlockHeight != 0) {
+ trade.setOpenDisputeTimeAsBlockHeight(publishDepositTxRequest.openDisputeTimeAsBlockHeight);
+ } else {
+ failed("waitPeriodForOpenDisputeAsBlockHeight = 0");
+ }
+
+ if (publishDepositTxRequest.checkPaymentTimeAsBlockHeight != 0) {
+ trade.setCheckPaymentTimeAsBlockHeight(publishDepositTxRequest.checkPaymentTimeAsBlockHeight);
+ } else {
+ failed("notificationTimeAsBlockHeight = 0");
+ }
+
+ // update to the latest peer address of our peer if the message is correct
+ trade.setTradingPeerAddress(processModel.getTempTradingPeerAddress());
+
+ complete();
+ } catch (Throwable t) {
+ failed(t);
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/SelectArbitrator.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/SelectArbitrator.java
new file mode 100644
index 0000000000..4b7aee180e
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/SelectArbitrator.java
@@ -0,0 +1,46 @@
+/*
+ * 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.trade.protocol.trade.tasks.taker;
+
+import io.bitsquare.common.taskrunner.TaskRunner;
+import io.bitsquare.trade.Trade;
+import io.bitsquare.trade.protocol.trade.ArbitrationSelectionRule;
+import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SelectArbitrator extends TradeTask {
+ private static final Logger log = LoggerFactory.getLogger(SelectArbitrator.class);
+
+ public SelectArbitrator(TaskRunner taskHandler, Trade trade) {
+ super(taskHandler, trade);
+ }
+
+ @Override
+ protected void run() {
+ try {
+ runInterceptHook();
+
+ trade.setArbitratorAddress(ArbitrationSelectionRule.select(processModel.getUser().getAcceptedArbitratorAddresses(), processModel.getOffer()));
+
+ complete();
+ } catch (Throwable t) {
+ failed(t);
+ }
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/SendDepositTxPublishedMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/SendDepositTxPublishedMessage.java
new file mode 100644
index 0000000000..76da9d0106
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/SendDepositTxPublishedMessage.java
@@ -0,0 +1,80 @@
+/*
+ * 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.trade.protocol.trade.tasks.taker;
+
+import io.bitsquare.common.taskrunner.TaskRunner;
+import io.bitsquare.p2p.messaging.SendMailboxMessageListener;
+import io.bitsquare.trade.Trade;
+import io.bitsquare.trade.protocol.trade.messages.DepositTxPublishedMessage;
+import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SendDepositTxPublishedMessage extends TradeTask {
+ private static final Logger log = LoggerFactory.getLogger(SendDepositTxPublishedMessage.class);
+
+ public SendDepositTxPublishedMessage(TaskRunner taskHandler, Trade trade) {
+ super(taskHandler, trade);
+ }
+
+ @Override
+ protected void run() {
+ try {
+ runInterceptHook();
+ if (trade.getDepositTx() != null) {
+ DepositTxPublishedMessage tradeMessage = new DepositTxPublishedMessage(processModel.getId(),
+ trade.getDepositTx().bitcoinSerialize(),
+ processModel.getMyAddress());
+
+ processModel.getP2PService().sendEncryptedMailboxMessage(
+ trade.getTradingPeerAddress(),
+ processModel.tradingPeer.getPubKeyRing(),
+ tradeMessage,
+ new SendMailboxMessageListener() {
+ @Override
+ public void onArrived() {
+ log.trace("Message arrived at peer.");
+ trade.setState(Trade.State.DEPOSIT_PUBLISHED_MSG_SENT);
+ complete();
+ }
+
+ @Override
+ public void onStoredInMailbox() {
+ log.trace("Message stored in mailbox.");
+ trade.setState(Trade.State.DEPOSIT_PUBLISHED_MSG_SENT);
+ complete();
+ }
+
+ @Override
+ public void onFault() {
+ appendToErrorMessage("DepositTxPublishedMessage sending failed");
+ failed();
+ }
+ }
+ );
+ } else {
+ log.error("trade.getDepositTx() = " + trade.getDepositTx());
+ failed("DepositTx is null");
+ }
+ } catch (Throwable t) {
+ failed(t);
+ }
+ }
+
+
+}
diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/SendPayDepositRequest.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/SendPayDepositRequest.java
new file mode 100644
index 0000000000..c05d9fc066
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/SendPayDepositRequest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.trade.protocol.trade.tasks.taker;
+
+import io.bitsquare.common.taskrunner.TaskRunner;
+import io.bitsquare.p2p.messaging.SendMailboxMessageListener;
+import io.bitsquare.trade.Trade;
+import io.bitsquare.trade.protocol.trade.messages.PayDepositRequest;
+import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SendPayDepositRequest extends TradeTask {
+ private static final Logger log = LoggerFactory.getLogger(SendPayDepositRequest.class);
+
+ public SendPayDepositRequest(TaskRunner taskHandler, Trade trade) {
+ super(taskHandler, trade);
+ }
+
+ @Override
+ protected void run() {
+ try {
+ runInterceptHook();
+ if (processModel.getTakeOfferFeeTx() != null) {
+ PayDepositRequest payDepositRequest = new PayDepositRequest(
+ processModel.getMyAddress(),
+ processModel.getId(),
+ trade.getTradeAmount().value,
+ processModel.getRawInputs(),
+ processModel.getChangeOutputValue(),
+ processModel.getChangeOutputAddress(),
+ processModel.getTradeWalletPubKey(),
+ processModel.getAddressEntry().getAddressString(),
+ processModel.getPubKeyRing(),
+ processModel.getPaymentAccountContractData(trade),
+ processModel.getAccountId(),
+ processModel.getTakeOfferFeeTx().getHashAsString(),
+ processModel.getUser().getAcceptedArbitratorAddresses(),
+ trade.getArbitratorAddress()
+ );
+
+ processModel.getP2PService().sendEncryptedMailboxMessage(
+ trade.getTradingPeerAddress(),
+ processModel.tradingPeer.getPubKeyRing(),
+ payDepositRequest,
+ new SendMailboxMessageListener() {
+ @Override
+ public void onArrived() {
+ log.trace("Message arrived at peer.");
+ complete();
+ }
+
+ @Override
+ public void onStoredInMailbox() {
+ log.trace("Message stored in mailbox.");
+ complete();
+ }
+
+ @Override
+ public void onFault() {
+ appendToErrorMessage("PayDepositRequest sending failed");
+ failed();
+ }
+ }
+ );
+ } else {
+ log.error("processModel.getTakeOfferFeeTx() = " + processModel.getTakeOfferFeeTx());
+ failed("TakeOfferFeeTx is null");
+ }
+ } catch (Throwable t) {
+ failed(t);
+ }
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/VerifyAndSignContract.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/VerifyAndSignContract.java
new file mode 100644
index 0000000000..32396a0c06
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/VerifyAndSignContract.java
@@ -0,0 +1,98 @@
+/*
+ * 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.trade.protocol.trade.tasks.taker;
+
+import io.bitsquare.common.crypto.CryptoUtil;
+import io.bitsquare.common.taskrunner.TaskRunner;
+import io.bitsquare.common.util.Utilities;
+import io.bitsquare.p2p.Address;
+import io.bitsquare.payment.PaymentAccountContractData;
+import io.bitsquare.trade.Contract;
+import io.bitsquare.trade.SellerAsTakerTrade;
+import io.bitsquare.trade.Trade;
+import io.bitsquare.trade.protocol.trade.TradingPeer;
+import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class VerifyAndSignContract extends TradeTask {
+ private static final Logger log = LoggerFactory.getLogger(VerifyAndSignContract.class);
+
+ public VerifyAndSignContract(TaskRunner taskHandler, Trade trade) {
+ super(taskHandler, trade);
+ }
+
+ @Override
+ protected void run() {
+ try {
+ runInterceptHook();
+
+ if (processModel.getTakeOfferFeeTx() != null) {
+ TradingPeer offerer = processModel.tradingPeer;
+ PaymentAccountContractData offererPaymentAccountContractData = offerer.getPaymentAccountContractData();
+ PaymentAccountContractData takerPaymentAccountContractData = processModel.getPaymentAccountContractData(trade);
+
+ boolean isBuyerOffererOrSellerTaker = trade instanceof SellerAsTakerTrade;
+ Address buyerAddress = isBuyerOffererOrSellerTaker ? processModel.getTempTradingPeerAddress() : processModel.getMyAddress();
+ Address sellerAddress = isBuyerOffererOrSellerTaker ? processModel.getMyAddress() : processModel.getTempTradingPeerAddress();
+ log.debug("isBuyerOffererOrSellerTaker " + isBuyerOffererOrSellerTaker);
+ log.debug("buyerAddress " + buyerAddress);
+ log.debug("sellerAddress " + sellerAddress);
+
+ Contract contract = new Contract(
+ processModel.getOffer(),
+ trade.getTradeAmount(),
+ processModel.getTakeOfferFeeTx().getHashAsString(),
+ buyerAddress,
+ sellerAddress,
+ trade.getArbitratorAddress(),
+ isBuyerOffererOrSellerTaker,
+ offerer.getAccountId(),
+ processModel.getAccountId(),
+ offererPaymentAccountContractData,
+ takerPaymentAccountContractData,
+ offerer.getPubKeyRing(),
+ processModel.getPubKeyRing(),
+ offerer.getPayoutAddressString(),
+ processModel.getAddressEntry().getAddressString(),
+ offerer.getTradeWalletPubKey(),
+ processModel.getTradeWalletPubKey()
+ );
+ String contractAsJson = Utilities.objectToJson(contract);
+ String signature = CryptoUtil.signMessage(processModel.getKeyRing().getMsgSignatureKeyPair().getPrivate(), contractAsJson);
+ trade.setContract(contract);
+ trade.setContractAsJson(contractAsJson);
+ trade.setTakerContractSignature(signature);
+
+ try {
+ CryptoUtil.verifyMessage(offerer.getPubKeyRing().getMsgSignaturePubKey(),
+ contractAsJson,
+ offerer.getContractSignature());
+ } catch (Throwable t) {
+ failed("Signature verification failed. " + t.getMessage());
+ }
+
+ complete();
+ } else {
+ failed("processModel.getTakeOfferFeeTx() = null");
+ }
+ } catch (Throwable t) {
+ failed(t);
+ }
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/user/BlockChainExplorer.java b/core/src/main/java/io/bitsquare/user/BlockChainExplorer.java
new file mode 100644
index 0000000000..bfe8ae5909
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/user/BlockChainExplorer.java
@@ -0,0 +1,41 @@
+/*
+ * 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.user;
+
+import io.bitsquare.app.Version;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.Serializable;
+
+public class BlockChainExplorer implements Serializable {
+ // That object is saved to disc. We need to take care of changes to not break deserialization.
+ private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
+
+ private static final Logger log = LoggerFactory.getLogger(BlockChainExplorer.class);
+
+ public final String name;
+ public final String txUrl;
+ public final String addressUrl;
+
+ public BlockChainExplorer(String name, String txUrl, String addressUrl) {
+ this.name = name;
+ this.txUrl = txUrl;
+ this.addressUrl = addressUrl;
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/user/PopupId.java b/core/src/main/java/io/bitsquare/user/PopupId.java
new file mode 100644
index 0000000000..47cce412b4
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/user/PopupId.java
@@ -0,0 +1,27 @@
+/*
+ * 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.user;
+
+public class PopupId {
+
+ // We don't use an enum because it would break updates if we add a new item in a new version
+
+ public static String SEC_DEPOSIT = "SEC_DEPOSIT";
+ public static String TRADE_WALLET = "TRADE_WALLET";
+
+}
diff --git a/core/src/main/resources/logback.xml b/core/src/main/resources/logback.xml
new file mode 100644
index 0000000000..528b3cfd2b
--- /dev/null
+++ b/core/src/main/resources/logback.xml
@@ -0,0 +1,63 @@
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{15} - %msg %xEx%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/gui/src/main/java/io/bitsquare/gui/common/model/ActivatableDataModel.java b/gui/src/main/java/io/bitsquare/gui/common/model/ActivatableDataModel.java
new file mode 100644
index 0000000000..edc1f7c2f9
--- /dev/null
+++ b/gui/src/main/java/io/bitsquare/gui/common/model/ActivatableDataModel.java
@@ -0,0 +1,41 @@
+/*
+ * 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.gui.common.model;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class ActivatableDataModel implements Activatable, DataModel {
+ protected final Logger log = LoggerFactory.getLogger(this.getClass());
+
+ @Override
+ public final void _activate() {
+ this.activate();
+ }
+
+ protected void activate() {
+ }
+
+ @Override
+ public final void _deactivate() {
+ this.deactivate();
+ }
+
+ protected void deactivate() {
+ }
+}
diff --git a/gui/src/main/java/io/bitsquare/gui/common/model/ActivatableViewModel.java b/gui/src/main/java/io/bitsquare/gui/common/model/ActivatableViewModel.java
new file mode 100644
index 0000000000..dc12f2cf5e
--- /dev/null
+++ b/gui/src/main/java/io/bitsquare/gui/common/model/ActivatableViewModel.java
@@ -0,0 +1,41 @@
+/*
+ * 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.gui.common.model;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class ActivatableViewModel implements Activatable, ViewModel {
+ protected final Logger log = LoggerFactory.getLogger(this.getClass());
+
+ @Override
+ public final void _activate() {
+ this.activate();
+ }
+
+ protected void activate() {
+ }
+
+ @Override
+ public final void _deactivate() {
+ this.deactivate();
+ }
+
+ protected void deactivate() {
+ }
+}
diff --git a/gui/src/main/java/io/bitsquare/gui/components/PasswordTextField.java b/gui/src/main/java/io/bitsquare/gui/components/PasswordTextField.java
new file mode 100644
index 0000000000..b9ae94265b
--- /dev/null
+++ b/gui/src/main/java/io/bitsquare/gui/components/PasswordTextField.java
@@ -0,0 +1,172 @@
+/*
+ * 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.gui.components;
+
+import io.bitsquare.gui.util.validation.InputValidator;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.geometry.Insets;
+import javafx.geometry.Point2D;
+import javafx.scene.control.Label;
+import javafx.scene.control.PasswordField;
+import javafx.scene.effect.BlurType;
+import javafx.scene.effect.DropShadow;
+import javafx.scene.effect.Effect;
+import javafx.scene.layout.Region;
+import javafx.scene.paint.Color;
+import javafx.stage.Window;
+import org.controlsfx.control.PopOver;
+
+public class PasswordTextField extends PasswordField {
+
+ private final Effect invalidEffect = new DropShadow(BlurType.THREE_PASS_BOX, Color.RED, 4, 0.0, 0, 0);
+
+ private final ObjectProperty validationResult = new SimpleObjectProperty<>
+ (new InputValidator.ValidationResult(true));
+
+ private static PopOver errorMessageDisplay;
+ private Region layoutReference = this;
+
+ public InputValidator getValidator() {
+ return validator;
+ }
+
+ public void setValidator(InputValidator validator) {
+ this.validator = validator;
+ }
+
+ private InputValidator validator;
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Static
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ private static void hideErrorMessageDisplay() {
+ if (errorMessageDisplay != null)
+ errorMessageDisplay.hide();
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Constructor
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ public PasswordTextField() {
+ super();
+
+ validationResult.addListener((ov, oldValue, newValue) -> {
+ if (newValue != null) {
+ setEffect(newValue.isValid ? null : invalidEffect);
+
+ if (newValue.isValid)
+ hideErrorMessageDisplay();
+ else
+ applyErrorMessage(newValue);
+ }
+ });
+
+ sceneProperty().addListener((ov, oldValue, newValue) -> {
+ // we got removed from the scene so hide the popup (if open)
+ if (newValue == null)
+ hideErrorMessageDisplay();
+ });
+
+ focusedProperty().addListener((o, oldValue, newValue) -> {
+ if (oldValue && !newValue && validator != null)
+ validationResult.set(validator.validate(getText()));
+ });
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Public methods
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ public void resetValidation() {
+ setEffect(null);
+ hideErrorMessageDisplay();
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Setters
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * @param layoutReference The node used as reference for positioning. If not set explicitly the
+ * ValidatingTextField instance is used.
+ */
+ public void setLayoutReference(Region layoutReference) {
+ this.layoutReference = layoutReference;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Getters
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ public ObjectProperty validationResultProperty() {
+ return validationResult;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Private methods
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ private void applyErrorMessage(InputValidator.ValidationResult validationResult) {
+ if (errorMessageDisplay != null)
+ errorMessageDisplay.hide();
+
+ if (!validationResult.isValid) {
+ createErrorPopOver(validationResult.errorMessage);
+ if (getScene() != null)
+ errorMessageDisplay.show(getScene().getWindow(), getErrorPopupPosition().getX(),
+ getErrorPopupPosition().getY());
+
+ if (errorMessageDisplay != null)
+ errorMessageDisplay.setDetached(false);
+ }
+ }
+
+ private Point2D getErrorPopupPosition() {
+ Window window = getScene().getWindow();
+ Point2D point;
+ point = layoutReference.localToScene(0, 0);
+ double x = Math.floor(point.getX() + window.getX() + layoutReference.getWidth() + 20 - getPadding().getLeft() -
+ getPadding().getRight());
+ double y = Math.floor(point.getY() + window.getY() + getHeight() / 2 - getPadding().getTop() - getPadding()
+ .getBottom());
+ return new Point2D(x, y);
+ }
+
+
+ private static void createErrorPopOver(String errorMessage) {
+ Label errorLabel = new Label(errorMessage);
+ errorLabel.setId("validation-error");
+ errorLabel.setPadding(new Insets(0, 10, 0, 10));
+ errorLabel.setOnMouseClicked(e -> hideErrorMessageDisplay());
+
+ errorMessageDisplay = new PopOver(errorLabel);
+ errorMessageDisplay.setDetachable(true);
+ errorMessageDisplay.setDetachedTitle("Close");
+ errorMessageDisplay.setArrowIndent(5);
+ }
+
+}
\ No newline at end of file
diff --git a/gui/src/main/java/io/bitsquare/gui/components/TableGroupHeadline.java b/gui/src/main/java/io/bitsquare/gui/components/TableGroupHeadline.java
new file mode 100644
index 0000000000..965a9e0354
--- /dev/null
+++ b/gui/src/main/java/io/bitsquare/gui/components/TableGroupHeadline.java
@@ -0,0 +1,84 @@
+/*
+ * 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.gui.components;
+
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+import javafx.geometry.Insets;
+import javafx.scene.control.Label;
+import javafx.scene.layout.GridPane;
+import javafx.scene.layout.Pane;
+import javafx.scene.layout.StackPane;
+
+public class TableGroupHeadline extends Pane {
+
+ private final Label label;
+ private final StringProperty text = new SimpleStringProperty();
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Constructor
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ public TableGroupHeadline() {
+ this("");
+ }
+
+ public TableGroupHeadline(String title) {
+ text.set(title);
+
+ GridPane.setMargin(this, new Insets(-10, -10, -10, -10));
+ GridPane.setColumnSpan(this, 2);
+
+ Pane bg = new StackPane();
+ bg.setId("table-group-headline");
+ bg.prefWidthProperty().bind(widthProperty());
+ bg.prefHeightProperty().bind(heightProperty());
+
+ label = new Label();
+ label.textProperty().bind(text);
+ label.setLayoutX(8);
+ label.setLayoutY(-8);
+ label.setPadding(new Insets(0, 7, 0, 5));
+ setActive();
+ getChildren().addAll(bg, label);
+ }
+
+ public void setInactive() {
+ setId("titled-group-bg");
+ label.setId("titled-group-bg-label");
+ }
+
+ private void setActive() {
+ setId("titled-group-bg-active");
+ label.setId("titled-group-bg-label-active");
+ }
+
+ public String getText() {
+ return text.get();
+ }
+
+ public StringProperty textProperty() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text.set(text);
+ }
+
+
+}
diff --git a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/AliPayForm.java b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/AliPayForm.java
new file mode 100644
index 0000000000..f1f4267a41
--- /dev/null
+++ b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/AliPayForm.java
@@ -0,0 +1,98 @@
+/*
+ * 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.gui.components.paymentmethods;
+
+import io.bitsquare.gui.components.InputTextField;
+import io.bitsquare.gui.util.Layout;
+import io.bitsquare.gui.util.validation.AliPayValidator;
+import io.bitsquare.gui.util.validation.InputValidator;
+import io.bitsquare.locale.BSResources;
+import io.bitsquare.payment.AliPayAccount;
+import io.bitsquare.payment.AliPayAccountContractData;
+import io.bitsquare.payment.PaymentAccount;
+import io.bitsquare.payment.PaymentAccountContractData;
+import javafx.scene.layout.GridPane;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static io.bitsquare.gui.util.FormBuilder.*;
+
+public class AliPayForm extends PaymentMethodForm {
+ private static final Logger log = LoggerFactory.getLogger(AliPayForm.class);
+
+ private final AliPayAccount aliPayAccount;
+ private final AliPayValidator aliPayValidator;
+ private InputTextField accountNrInputTextField;
+
+ public static int addFormForBuyer(GridPane gridPane, int gridRow, PaymentAccountContractData paymentAccountContractData) {
+ addLabelTextField(gridPane, ++gridRow, "Payment method:", BSResources.get(paymentAccountContractData.getPaymentMethodName()));
+ addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Account nr.:", ((AliPayAccountContractData) paymentAccountContractData).getAccountNr());
+ addAllowedPeriod(gridPane, ++gridRow, paymentAccountContractData);
+ return gridRow;
+ }
+
+ public AliPayForm(PaymentAccount paymentAccount, AliPayValidator aliPayValidator, InputValidator inputValidator, GridPane gridPane, int gridRow) {
+ super(paymentAccount, inputValidator, gridPane, gridRow);
+ this.aliPayAccount = (AliPayAccount) paymentAccount;
+ this.aliPayValidator = aliPayValidator;
+ }
+
+ @Override
+ public void addFormForAddAccount() {
+ gridRowFrom = gridRow + 1;
+
+ accountNrInputTextField = addLabelInputTextField(gridPane, ++gridRow, "Account nr.:").second;
+ accountNrInputTextField.setValidator(aliPayValidator);
+ accountNrInputTextField.textProperty().addListener((ov, oldValue, newValue) -> {
+ aliPayAccount.setAccountNr(newValue);
+ updateFromInputs();
+ });
+
+ addLabelTextField(gridPane, ++gridRow, "Currency:", aliPayAccount.getSingleTradeCurrency().getCodeAndName());
+ addAllowedPeriod();
+ addAccountNameTextFieldWithAutoFillCheckBox();
+ }
+
+ @Override
+ protected void autoFillNameTextField() {
+ if (autoFillCheckBox != null && autoFillCheckBox.isSelected()) {
+ String accountNr = accountNrInputTextField.getText();
+ accountNr = accountNr.substring(0, Math.min(5, accountNr.length())) + "...";
+ String method = BSResources.get(paymentAccount.getPaymentMethod().getId());
+ accountNameTextField.setText(method.concat(", ").concat(accountNr));
+ }
+ }
+
+ @Override
+ public void addFormForDisplayAccount() {
+ gridRowFrom = gridRow;
+ addLabelTextField(gridPane, gridRow, "Account name:", aliPayAccount.getAccountName(), Layout.FIRST_ROW_AND_GROUP_DISTANCE);
+ addLabelTextField(gridPane, ++gridRow, "Payment method:", BSResources.get(aliPayAccount.getPaymentMethod().getId()));
+ addLabelTextField(gridPane, ++gridRow, "Account nr.:", aliPayAccount.getAccountNr());
+ addLabelTextField(gridPane, ++gridRow, "Currency:", aliPayAccount.getSingleTradeCurrency().getCodeAndName());
+ addAllowedPeriod();
+ }
+
+ @Override
+ public void updateAllInputsValid() {
+ allInputsValid.set(isAccountNameValid()
+ && aliPayValidator.validate(aliPayAccount.getAccountNr()).isValid
+ && aliPayAccount.getTradeCurrencies().size() > 0);
+ }
+
+}
diff --git a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/BlockChainForm.java b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/BlockChainForm.java
new file mode 100644
index 0000000000..ef91de5276
--- /dev/null
+++ b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/BlockChainForm.java
@@ -0,0 +1,135 @@
+/*
+ * 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.gui.components.paymentmethods;
+
+import io.bitsquare.gui.components.InputTextField;
+import io.bitsquare.gui.util.Layout;
+import io.bitsquare.gui.util.validation.AltCoinAddressValidator;
+import io.bitsquare.gui.util.validation.InputValidator;
+import io.bitsquare.locale.BSResources;
+import io.bitsquare.locale.CurrencyUtil;
+import io.bitsquare.locale.TradeCurrency;
+import io.bitsquare.payment.BlockChainAccount;
+import io.bitsquare.payment.BlockChainAccountContractData;
+import io.bitsquare.payment.PaymentAccount;
+import io.bitsquare.payment.PaymentAccountContractData;
+import javafx.collections.FXCollections;
+import javafx.scene.control.ComboBox;
+import javafx.scene.layout.GridPane;
+import javafx.util.StringConverter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static io.bitsquare.gui.util.FormBuilder.*;
+
+public class BlockChainForm extends PaymentMethodForm {
+ private static final Logger log = LoggerFactory.getLogger(BlockChainForm.class);
+
+ private final BlockChainAccount blockChainAccount;
+ private final AltCoinAddressValidator altCoinAddressValidator;
+ private InputTextField addressInputTextField;
+
+ private ComboBox currencyComboBox;
+
+ public static int addFormForBuyer(GridPane gridPane, int gridRow, PaymentAccountContractData paymentAccountContractData) {
+ addLabelTextField(gridPane, ++gridRow, "Payment method:", BSResources.get(paymentAccountContractData.getPaymentMethodName()));
+ addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Address:", ((BlockChainAccountContractData) paymentAccountContractData).getAddress());
+ if (paymentAccountContractData instanceof BlockChainAccountContractData &&
+ ((BlockChainAccountContractData) paymentAccountContractData).getPaymentId() != null)
+ addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Payment ID:", ((BlockChainAccountContractData) paymentAccountContractData).getPaymentId());
+
+ addAllowedPeriod(gridPane, ++gridRow, paymentAccountContractData);
+ return gridRow;
+ }
+
+ public BlockChainForm(PaymentAccount paymentAccount, AltCoinAddressValidator altCoinAddressValidator, InputValidator inputValidator, GridPane gridPane,
+ int gridRow) {
+ super(paymentAccount, inputValidator, gridPane, gridRow);
+ this.blockChainAccount = (BlockChainAccount) paymentAccount;
+ this.altCoinAddressValidator = altCoinAddressValidator;
+ }
+
+ @Override
+ public void addFormForAddAccount() {
+ gridRowFrom = gridRow + 1;
+
+ addTradeCurrencyComboBox();
+ currencyComboBox.setPrefWidth(250);
+ addressInputTextField = addLabelInputTextField(gridPane, ++gridRow, "Address:").second;
+ addressInputTextField.setValidator(altCoinAddressValidator);
+
+ addressInputTextField.textProperty().addListener((ov, oldValue, newValue) -> {
+ blockChainAccount.setAddress(newValue);
+ updateFromInputs();
+ });
+
+ addAllowedPeriod();
+ addAccountNameTextFieldWithAutoFillCheckBox();
+ }
+
+ @Override
+ protected void autoFillNameTextField() {
+ if (autoFillCheckBox != null && autoFillCheckBox.isSelected()) {
+ String method = BSResources.get(paymentAccount.getPaymentMethod().getId());
+ String address = addressInputTextField.getText();
+ address = address.substring(0, Math.min(9, address.length())) + "...";
+ String currency = paymentAccount.getSingleTradeCurrency() != null ? paymentAccount.getSingleTradeCurrency().getCode() : "?";
+ accountNameTextField.setText(method.concat(", ").concat(currency).concat(", ").concat(address));
+ }
+ }
+
+ @Override
+ public void addFormForDisplayAccount() {
+ gridRowFrom = gridRow;
+ addLabelTextField(gridPane, gridRow, "Account name:", blockChainAccount.getAccountName(), Layout.FIRST_ROW_AND_GROUP_DISTANCE);
+ addLabelTextField(gridPane, ++gridRow, "Payment method:", BSResources.get(blockChainAccount.getPaymentMethod().getId()));
+ addLabelTextField(gridPane, ++gridRow, "Address:", blockChainAccount.getAddress());
+ addLabelTextField(gridPane, ++gridRow, "Crypto currency:", blockChainAccount.getSingleTradeCurrency().getCodeAndName());
+ addAllowedPeriod();
+ }
+
+ @Override
+ public void updateAllInputsValid() {
+ allInputsValid.set(isAccountNameValid()
+ && altCoinAddressValidator.validate(blockChainAccount.getAddress()).isValid
+ && blockChainAccount.getSingleTradeCurrency() != null);
+ }
+
+ @Override
+ protected void addTradeCurrencyComboBox() {
+ currencyComboBox = addLabelComboBox(gridPane, ++gridRow, "Crypto currency:").second;
+ currencyComboBox.setPromptText("Select crypto currency");
+ currencyComboBox.setItems(FXCollections.observableArrayList(CurrencyUtil.getSortedCryptoCurrencies()));
+ currencyComboBox.setVisibleRowCount(Math.min(currencyComboBox.getItems().size(), 20));
+ currencyComboBox.setConverter(new StringConverter() {
+ @Override
+ public String toString(TradeCurrency tradeCurrency) {
+ return tradeCurrency.getCodeAndName();
+ }
+
+ @Override
+ public TradeCurrency fromString(String s) {
+ return null;
+ }
+ });
+ currencyComboBox.setOnAction(e -> {
+ paymentAccount.setSingleTradeCurrency(currencyComboBox.getSelectionModel().getSelectedItem());
+ updateFromInputs();
+ });
+ }
+}
diff --git a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/OKPayForm.java b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/OKPayForm.java
new file mode 100644
index 0000000000..d235a41953
--- /dev/null
+++ b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/OKPayForm.java
@@ -0,0 +1,140 @@
+/*
+ * 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.gui.components.paymentmethods;
+
+import io.bitsquare.gui.components.InputTextField;
+import io.bitsquare.gui.util.Layout;
+import io.bitsquare.gui.util.validation.InputValidator;
+import io.bitsquare.gui.util.validation.OKPayValidator;
+import io.bitsquare.locale.BSResources;
+import io.bitsquare.locale.CurrencyUtil;
+import io.bitsquare.payment.OKPayAccount;
+import io.bitsquare.payment.OKPayAccountContractData;
+import io.bitsquare.payment.PaymentAccount;
+import io.bitsquare.payment.PaymentAccountContractData;
+import javafx.geometry.Insets;
+import javafx.geometry.VPos;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.Label;
+import javafx.scene.layout.FlowPane;
+import javafx.scene.layout.GridPane;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static io.bitsquare.gui.util.FormBuilder.*;
+
+public class OKPayForm extends PaymentMethodForm {
+ private static final Logger log = LoggerFactory.getLogger(OKPayForm.class);
+
+ private final OKPayAccount okPayAccount;
+ private final OKPayValidator okPayValidator;
+ private InputTextField accountNrInputTextField;
+
+ public static int addFormForBuyer(GridPane gridPane, int gridRow, PaymentAccountContractData paymentAccountContractData) {
+ addLabelTextField(gridPane, ++gridRow, "Payment method:", BSResources.get(paymentAccountContractData.getPaymentMethodName()));
+ addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Account nr.:", ((OKPayAccountContractData) paymentAccountContractData).getAccountNr());
+ addAllowedPeriod(gridPane, ++gridRow, paymentAccountContractData);
+ return gridRow;
+ }
+
+ public OKPayForm(PaymentAccount paymentAccount, OKPayValidator okPayValidator, InputValidator inputValidator, GridPane gridPane, int gridRow) {
+ super(paymentAccount, inputValidator, gridPane, gridRow);
+ this.okPayAccount = (OKPayAccount) paymentAccount;
+ this.okPayValidator = okPayValidator;
+ }
+
+ @Override
+ public void addFormForAddAccount() {
+ gridRowFrom = gridRow + 1;
+
+ accountNrInputTextField = addLabelInputTextField(gridPane, ++gridRow, "Account nr.:").second;
+ accountNrInputTextField.setValidator(okPayValidator);
+ accountNrInputTextField.textProperty().addListener((ov, oldValue, newValue) -> {
+ okPayAccount.setAccountNr(newValue);
+ updateFromInputs();
+ });
+
+ addCurrenciesGrid(true);
+ addAllowedPeriod();
+ addAccountNameTextFieldWithAutoFillCheckBox();
+ }
+
+ private void addCurrenciesGrid(boolean isEditable) {
+ Label label = addLabel(gridPane, ++gridRow, "Supported OKPay currencies:", 0);
+ GridPane.setValignment(label, VPos.TOP);
+ FlowPane flowPane = new FlowPane();
+ flowPane.setPadding(new Insets(10, 10, 10, 10));
+ flowPane.setVgap(10);
+ flowPane.setHgap(10);
+
+ if (isEditable)
+ flowPane.setId("flowpane-checkboxes-bg");
+ else
+ flowPane.setId("flowpane-checkboxes-non-editable-bg");
+
+ CurrencyUtil.getAllOKPayCurrencies().stream().forEach(e ->
+ {
+ CheckBox checkBox = new CheckBox(e.getCode());
+ checkBox.setMouseTransparent(!isEditable);
+ checkBox.setSelected(okPayAccount.getTradeCurrencies().contains(e));
+ checkBox.setMinWidth(60);
+ checkBox.setMaxWidth(checkBox.getMinWidth());
+ checkBox.setOnAction(event -> {
+ if (checkBox.isSelected())
+ okPayAccount.addCurrency(e);
+ else
+ okPayAccount.removeCurrency(e);
+
+ updateAllInputsValid();
+ });
+ flowPane.getChildren().add(checkBox);
+ });
+
+ GridPane.setRowIndex(flowPane, gridRow);
+ GridPane.setColumnIndex(flowPane, 1);
+ gridPane.getChildren().add(flowPane);
+ }
+
+ @Override
+ protected void autoFillNameTextField() {
+ if (autoFillCheckBox != null && autoFillCheckBox.isSelected()) {
+ String accountNr = accountNrInputTextField.getText();
+ accountNr = accountNr.substring(0, Math.min(5, accountNr.length())) + "...";
+ String method = BSResources.get(paymentAccount.getPaymentMethod().getId());
+ accountNameTextField.setText(method.concat(", ").concat(accountNr));
+ }
+ }
+
+ @Override
+ public void addFormForDisplayAccount() {
+ gridRowFrom = gridRow;
+ addLabelTextField(gridPane, gridRow, "Account name:", okPayAccount.getAccountName(), Layout.FIRST_ROW_AND_GROUP_DISTANCE);
+ addLabelTextField(gridPane, ++gridRow, "Payment method:", BSResources.get(okPayAccount.getPaymentMethod().getId()));
+ addLabelTextField(gridPane, ++gridRow, "Account nr.:", okPayAccount.getAccountNr());
+ addAllowedPeriod();
+ addCurrenciesGrid(false);
+ }
+
+ @Override
+ public void updateAllInputsValid() {
+ allInputsValid.set(isAccountNameValid()
+ && okPayValidator.validate(okPayAccount.getAccountNr()).isValid
+ && okPayAccount.getTradeCurrencies().size() > 0);
+ }
+
+}
diff --git a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/PaymentMethodForm.java b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/PaymentMethodForm.java
new file mode 100644
index 0000000000..4ea4ef6c8c
--- /dev/null
+++ b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/PaymentMethodForm.java
@@ -0,0 +1,154 @@
+/*
+ * 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.gui.components.paymentmethods;
+
+import io.bitsquare.common.util.Tuple3;
+import io.bitsquare.gui.components.InputTextField;
+import io.bitsquare.gui.util.validation.InputValidator;
+import io.bitsquare.locale.CurrencyUtil;
+import io.bitsquare.locale.TradeCurrency;
+import io.bitsquare.payment.PaymentAccount;
+import io.bitsquare.payment.PaymentAccountContractData;
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.collections.FXCollections;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.ComboBox;
+import javafx.scene.control.Label;
+import javafx.scene.layout.GridPane;
+import javafx.util.StringConverter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static io.bitsquare.gui.util.FormBuilder.*;
+
+public abstract class PaymentMethodForm {
+ private static final Logger log = LoggerFactory.getLogger(PaymentMethodForm.class);
+
+ protected final PaymentAccount paymentAccount;
+ protected final InputValidator inputValidator;
+ protected final GridPane gridPane;
+ protected int gridRow;
+ protected final BooleanProperty allInputsValid = new SimpleBooleanProperty();
+
+ protected int gridRowFrom;
+ protected InputTextField accountNameTextField;
+ protected CheckBox autoFillCheckBox;
+ private ComboBox currencyComboBox;
+
+ public PaymentMethodForm(PaymentAccount paymentAccount, InputValidator inputValidator, GridPane gridPane, int gridRow) {
+ this.paymentAccount = paymentAccount;
+ this.inputValidator = inputValidator;
+ this.gridPane = gridPane;
+ this.gridRow = gridRow;
+ }
+
+ protected void addTradeCurrencyComboBox() {
+ currencyComboBox = addLabelComboBox(gridPane, ++gridRow, "Currency:").second;
+ currencyComboBox.setPromptText("Select currency");
+ currencyComboBox.setItems(FXCollections.observableArrayList(CurrencyUtil.getAllSortedCurrencies()));
+ currencyComboBox.setConverter(new StringConverter() {
+ @Override
+ public String toString(TradeCurrency tradeCurrency) {
+ return tradeCurrency.getCodeAndName();
+ }
+
+ @Override
+ public TradeCurrency fromString(String s) {
+ return null;
+ }
+ });
+ currencyComboBox.setOnAction(e -> {
+ paymentAccount.setSingleTradeCurrency(currencyComboBox.getSelectionModel().getSelectedItem());
+ updateFromInputs();
+ });
+ }
+
+ protected void addAccountNameTextFieldWithAutoFillCheckBox() {
+ Tuple3