invalidationListener;
private String fiatCode;
@@ -68,14 +68,14 @@ public class OfferBook {
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
- OfferBook(OfferRepository offerRepository, User user) {
- this.offerRepository = offerRepository;
+ OfferBook(RemoteOfferBook remoteOfferBook, User user) {
+ this.remoteOfferBook = remoteOfferBook;
this.user = user;
bankAccountChangeListener = (observableValue, oldValue, newValue) -> setBankAccount(newValue);
invalidationListener = (ov, oldValue, newValue) -> requestOffers();
- offerRepositoryListener = new OfferRepository.Listener() {
+ remoteOfferBookListener = new RemoteOfferBook.Listener() {
@Override
public void onOfferAdded(Offer offer) {
addOfferToOfferBookListItems(offer);
@@ -142,15 +142,15 @@ public class OfferBook {
private void addListeners() {
log.debug("addListeners ");
user.currentBankAccountProperty().addListener(bankAccountChangeListener);
- offerRepository.addListener(offerRepositoryListener);
- offerRepository.invalidationTimestampProperty().addListener(invalidationListener);
+ remoteOfferBook.addListener(remoteOfferBookListener);
+ remoteOfferBook.invalidationTimestampProperty().addListener(invalidationListener);
}
private void removeListeners() {
log.debug("removeListeners ");
user.currentBankAccountProperty().removeListener(bankAccountChangeListener);
- offerRepository.removeListener(offerRepositoryListener);
- offerRepository.invalidationTimestampProperty().removeListener(invalidationListener);
+ remoteOfferBook.removeListener(remoteOfferBookListener);
+ remoteOfferBook.invalidationTimestampProperty().removeListener(invalidationListener);
}
private void addOfferToOfferBookListItems(Offer offer) {
@@ -160,7 +160,7 @@ public class OfferBook {
}
private void requestOffers() {
- offerRepository.getOffers(fiatCode);
+ remoteOfferBook.getOffers(fiatCode);
}
@@ -173,11 +173,11 @@ public class OfferBook {
addListeners();
setBankAccount(user.getCurrentBankAccount().get());
pollingTimer = Utilities.setInterval(3000, (animationTimer) -> {
- offerRepository.requestInvalidationTimeStampFromDHT(fiatCode);
+ remoteOfferBook.requestInvalidationTimeStampFromDHT(fiatCode);
return null;
});
- offerRepository.getOffers(fiatCode);
+ remoteOfferBook.getOffers(fiatCode);
}
private void stopPolling() {
diff --git a/gui/src/main/java/io/bitsquare/msg/tomp2p/BootstrappedPeerBuilder.java b/gui/src/main/java/io/bitsquare/msg/tomp2p/BootstrappedPeerBuilder.java
index 1756ba825c..93d8c016c1 100644
--- a/gui/src/main/java/io/bitsquare/msg/tomp2p/BootstrappedPeerBuilder.java
+++ b/gui/src/main/java/io/bitsquare/msg/tomp2p/BootstrappedPeerBuilder.java
@@ -68,7 +68,7 @@ import io.netty.util.concurrent.DefaultEventExecutorGroup;
/**
* Creates a DHT peer and bootstraps to the network via a bootstrap node
*/
-class BootstrappedPeerBuilder {
+public class BootstrappedPeerBuilder {
private static final Logger log = LoggerFactory.getLogger(BootstrappedPeerBuilder.class);
static final String BOOTSTRAP_NODE_KEY = "bootstrapNode";
diff --git a/gui/src/main/java/io/bitsquare/msg/tomp2p/TomP2PMessageService.java b/gui/src/main/java/io/bitsquare/msg/tomp2p/TomP2PMessageService.java
index 7e9323b56d..8dd5be54ab 100644
--- a/gui/src/main/java/io/bitsquare/msg/tomp2p/TomP2PMessageService.java
+++ b/gui/src/main/java/io/bitsquare/msg/tomp2p/TomP2PMessageService.java
@@ -66,7 +66,7 @@ import rx.Observable;
*
* TODO: improve callbacks that Platform.runLater is not necessary. We call usually that methods form teh UI thread.
*/
-class TomP2PMessageService implements MessageService {
+public class TomP2PMessageService implements MessageService {
private static final Logger log = LoggerFactory.getLogger(TomP2PMessageService.class);
private static final String ARBITRATORS_ROOT = "ArbitratorsRoot";
diff --git a/gui/src/main/java/io/bitsquare/network/BootstrapNodes.java b/gui/src/main/java/io/bitsquare/network/BootstrapNodes.java
index bb2713a175..70643018b0 100644
--- a/gui/src/main/java/io/bitsquare/network/BootstrapNodes.java
+++ b/gui/src/main/java/io/bitsquare/network/BootstrapNodes.java
@@ -30,7 +30,7 @@ public interface BootstrapNodes {
Node DEFAULT = DIGITAL_OCEAN_1;
/**
- * A locally-running {@link io.bitsquare.app.cli.BootstrapNode} instance.
+ * A locally-running {@link io.bitsquare.app.bootstrap.BootstrapNode} instance.
* Typically used only for testing. Not included in results from {@link #all()}.
*/
Node LOCALHOST = Node.at("localhost", "127.0.0.1");
diff --git a/gui/src/main/java/io/bitsquare/offer/OfferModule.java b/gui/src/main/java/io/bitsquare/offer/OfferModule.java
index 227c30656b..dc3be4bbab 100644
--- a/gui/src/main/java/io/bitsquare/offer/OfferModule.java
+++ b/gui/src/main/java/io/bitsquare/offer/OfferModule.java
@@ -27,10 +27,4 @@ public abstract class OfferModule extends BitsquareModule {
super(env);
}
- @Override
- protected void configure() {
- bind(OfferRepository.class).to(offerRepository()).asEagerSingleton();
- }
-
- protected abstract Class extends OfferRepository> offerRepository();
}
diff --git a/gui/src/main/java/io/bitsquare/offer/OfferRepository.java b/gui/src/main/java/io/bitsquare/offer/RemoteOfferBook.java
similarity index 92%
rename from gui/src/main/java/io/bitsquare/offer/OfferRepository.java
rename to gui/src/main/java/io/bitsquare/offer/RemoteOfferBook.java
index 2b8c60f0c7..a643cc34e1 100644
--- a/gui/src/main/java/io/bitsquare/offer/OfferRepository.java
+++ b/gui/src/main/java/io/bitsquare/offer/RemoteOfferBook.java
@@ -21,10 +21,13 @@ import io.bitsquare.util.task.FaultHandler;
import io.bitsquare.util.task.ResultHandler;
import java.util.List;
+import java.util.concurrent.Executor;
import javafx.beans.property.LongProperty;
-public interface OfferRepository {
+public interface RemoteOfferBook {
+
+ void setExecutor(Executor executor);
void getOffers(String fiatCode);
diff --git a/gui/src/main/java/io/bitsquare/offer/tomp2p/TomP2POfferRepository.java b/gui/src/main/java/io/bitsquare/offer/tomp2p/TomP2POfferBook.java
similarity index 92%
rename from gui/src/main/java/io/bitsquare/offer/tomp2p/TomP2POfferRepository.java
rename to gui/src/main/java/io/bitsquare/offer/tomp2p/TomP2POfferBook.java
index bfb8c850ff..2ed6c7f756 100644
--- a/gui/src/main/java/io/bitsquare/offer/tomp2p/TomP2POfferRepository.java
+++ b/gui/src/main/java/io/bitsquare/offer/tomp2p/TomP2POfferBook.java
@@ -19,7 +19,7 @@ package io.bitsquare.offer.tomp2p;
import io.bitsquare.msg.tomp2p.TomP2PNode;
import io.bitsquare.offer.Offer;
-import io.bitsquare.offer.OfferRepository;
+import io.bitsquare.offer.RemoteOfferBook;
import io.bitsquare.util.task.FaultHandler;
import io.bitsquare.util.task.ResultHandler;
@@ -28,10 +28,10 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
-import javafx.application.Platform;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty;
@@ -48,20 +48,25 @@ import net.tomp2p.storage.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-class TomP2POfferRepository implements OfferRepository {
+public class TomP2POfferBook implements RemoteOfferBook {
- private static final Logger log = LoggerFactory.getLogger(TomP2POfferRepository.class);
+ private static final Logger log = LoggerFactory.getLogger(TomP2POfferBook.class);
private final List offerRepositoryListeners = new ArrayList<>();
private final LongProperty invalidationTimestamp = new SimpleLongProperty(0);
private final TomP2PNode p2pNode;
+ private Executor executor;
@Inject
- public TomP2POfferRepository(TomP2PNode p2pNode) {
+ public TomP2POfferBook(TomP2PNode p2pNode) {
this.p2pNode = p2pNode;
}
+ public void setExecutor(Executor executor) {
+ this.executor = executor;
+ }
+
@Override
public void addOffer(Offer offer, ResultHandler resultHandler, FaultHandler faultHandler) {
Number160 locationKey = Number160.createHash(offer.getCurrency().getCurrencyCode());
@@ -78,13 +83,13 @@ class TomP2POfferRepository implements OfferRepository {
@Override
public void operationComplete(BaseFuture future) throws Exception {
if (future.isSuccess()) {
- Platform.runLater(() -> {
+ executor.execute(() -> {
resultHandler.handleResult();
offerRepositoryListeners.stream().forEach(listener -> {
try {
Object offerDataObject = offerData.object();
if (offerDataObject instanceof Offer) {
- log.error("Added offer to DHT with ID: " + offerDataObject);
+ log.info("Added offer to DHT with ID: " + offerDataObject);
listener.onOfferAdded((Offer) offerDataObject);
}
} catch (ClassNotFoundException | IOException e) {
@@ -102,15 +107,11 @@ class TomP2POfferRepository implements OfferRepository {
@Override
public void exceptionCaught(Throwable ex) throws Exception {
- Platform.runLater(() -> {
- faultHandler.handleFault("Failed to add offer to DHT", ex);
- });
+ executor.execute(() -> faultHandler.handleFault("Failed to add offer to DHT", ex));
}
});
} catch (IOException ex) {
- Platform.runLater(() -> {
- faultHandler.handleFault("Failed to add offer to DHT", ex);
- });
+ executor.execute(() -> faultHandler.handleFault("Failed to add offer to DHT", ex));
}
}
@@ -130,7 +131,7 @@ class TomP2POfferRepository implements OfferRepository {
// it might change in future to something like foundAndRemoved and notFound
// See discussion at: https://github.com/tomp2p/TomP2P/issues/57#issuecomment-62069840
- Platform.runLater(() -> {
+ executor.execute(() -> {
offerRepositoryListeners.stream().forEach(listener -> {
try {
Object offerDataObject = offerData.object();
@@ -182,7 +183,7 @@ class TomP2POfferRepository implements OfferRepository {
}
}
- Platform.runLater(() -> offerRepositoryListeners.stream().forEach(listener ->
+ executor.execute(() -> offerRepositoryListeners.stream().forEach(listener ->
listener.onOffersReceived(offers)));
}
@@ -193,7 +194,7 @@ class TomP2POfferRepository implements OfferRepository {
final Map dataMap = futureGet.dataMap();
if (dataMap == null || dataMap.size() == 0) {
log.trace("Get offers from DHT delivered empty dataMap.");
- Platform.runLater(() -> offerRepositoryListeners.stream().forEach(listener ->
+ executor.execute(() -> offerRepositoryListeners.stream().forEach(listener ->
listener.onOffersReceived(new ArrayList<>())));
}
else {
@@ -262,7 +263,7 @@ class TomP2POfferRepository implements OfferRepository {
Data data = futureGet.data();
if (data != null && data.object() instanceof Long) {
final Object object = data.object();
- Platform.runLater(() -> {
+ executor.execute(() -> {
Long timeStamp = (Long) object;
//log.trace("Get invalidationTimestamp from DHT was successful. TimeStamp=" + timeStamp);
invalidationTimestamp.set(timeStamp);
diff --git a/gui/src/main/java/io/bitsquare/offer/tomp2p/TomP2POfferModule.java b/gui/src/main/java/io/bitsquare/offer/tomp2p/TomP2POfferModule.java
index af2262573d..935670be59 100644
--- a/gui/src/main/java/io/bitsquare/offer/tomp2p/TomP2POfferModule.java
+++ b/gui/src/main/java/io/bitsquare/offer/tomp2p/TomP2POfferModule.java
@@ -17,8 +17,15 @@
package io.bitsquare.offer.tomp2p;
+import io.bitsquare.msg.tomp2p.TomP2PNode;
import io.bitsquare.offer.OfferModule;
-import io.bitsquare.offer.OfferRepository;
+import io.bitsquare.offer.RemoteOfferBook;
+
+import com.google.inject.Provider;
+
+import javax.inject.Inject;
+
+import javafx.application.Platform;
import org.springframework.core.env.Environment;
@@ -29,7 +36,21 @@ public class TomP2POfferModule extends OfferModule {
}
@Override
- public Class extends OfferRepository> offerRepository() {
- return TomP2POfferRepository.class;
+ protected void configure() {
+ bind(RemoteOfferBook.class).toProvider(RemoteOfferBookProvider.class).asEagerSingleton();
}
}
+
+class RemoteOfferBookProvider implements Provider {
+ private final TomP2POfferBook remoteOfferBook;
+
+ @Inject
+ public RemoteOfferBookProvider(TomP2PNode p2pNode) {
+ remoteOfferBook = new TomP2POfferBook(p2pNode);
+ remoteOfferBook.setExecutor(Platform::runLater);
+ }
+
+ public RemoteOfferBook get() {
+ return remoteOfferBook;
+ }
+}
\ No newline at end of file
diff --git a/gui/src/main/java/io/bitsquare/trade/TradeManager.java b/gui/src/main/java/io/bitsquare/trade/TradeManager.java
index dd89ca4220..04ea0b0f73 100644
--- a/gui/src/main/java/io/bitsquare/trade/TradeManager.java
+++ b/gui/src/main/java/io/bitsquare/trade/TradeManager.java
@@ -28,10 +28,10 @@ import io.bitsquare.msg.listeners.OutgoingMessageListener;
import io.bitsquare.network.Peer;
import io.bitsquare.offer.Direction;
import io.bitsquare.offer.Offer;
-import io.bitsquare.offer.OfferRepository;
+import io.bitsquare.offer.RemoteOfferBook;
import io.bitsquare.persistence.Persistence;
import io.bitsquare.trade.handlers.TransactionResultHandler;
-import io.bitsquare.trade.protocol.createoffer.CreateOfferCoordinator;
+import io.bitsquare.trade.protocol.createoffer.CreateOfferProtocol;
import io.bitsquare.trade.protocol.trade.TradeMessage;
import io.bitsquare.trade.protocol.trade.offerer.BuyerAcceptsOfferProtocol;
import io.bitsquare.trade.protocol.trade.offerer.BuyerAcceptsOfferProtocolListener;
@@ -83,7 +83,7 @@ public class TradeManager {
private final BlockChainService blockChainService;
private final WalletService walletService;
private final SignatureService signatureService;
- private final OfferRepository offerRepository;
+ private final RemoteOfferBook remoteOfferBook;
//TODO store TakerAsSellerProtocol in trade
private final Map takerAsSellerProtocolMap = new HashMap<>();
@@ -106,7 +106,7 @@ public class TradeManager {
public TradeManager(User user, AccountSettings accountSettings, Persistence persistence,
MessageService messageService, BlockChainService blockChainService,
WalletService walletService, SignatureService signatureService,
- OfferRepository offerRepository) {
+ RemoteOfferBook remoteOfferBook) {
this.user = user;
this.accountSettings = accountSettings;
this.persistence = persistence;
@@ -114,7 +114,7 @@ public class TradeManager {
this.blockChainService = blockChainService;
this.walletService = walletService;
this.signatureService = signatureService;
- this.offerRepository = offerRepository;
+ this.remoteOfferBook = remoteOfferBook;
Object offersObject = persistence.read(this, "offers");
if (offersObject instanceof Map) {
@@ -172,7 +172,7 @@ public class TradeManager {
accountSettings.getAcceptedCountries(),
accountSettings.getAcceptedLanguageLocales());
- CreateOfferCoordinator createOfferCoordinator = new CreateOfferCoordinator(
+ CreateOfferProtocol createOfferCoordinator = new CreateOfferProtocol(
offer,
walletService,
(transactionId) -> {
@@ -186,9 +186,9 @@ public class TradeManager {
}
},
(message, throwable) -> errorMessageHandler.handleErrorMessage(message),
- offerRepository);
+ remoteOfferBook);
- createOfferCoordinator.start();
+ createOfferCoordinator.createOffer();
}
private void addOffer(Offer offer) {
@@ -206,7 +206,7 @@ public class TradeManager {
offers.remove(offer.getId());
persistOffers();
- offerRepository.removeOffer(offer);
+ remoteOfferBook.removeOffer(offer);
}
diff --git a/gui/src/main/java/io/bitsquare/trade/TradeMessage.java b/gui/src/main/java/io/bitsquare/trade/TradeMessage.java
new file mode 100644
index 0000000000..bc2e7ff492
--- /dev/null
+++ b/gui/src/main/java/io/bitsquare/trade/TradeMessage.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TradeMessage {
+ private static final Logger log = LoggerFactory.getLogger(TradeMessage.class);
+
+ public TradeMessage() {
+
+ }
+}
diff --git a/gui/src/main/java/io/bitsquare/trade/TradeState.java b/gui/src/main/java/io/bitsquare/trade/TradeState.java
new file mode 100644
index 0000000000..d3da1acc06
--- /dev/null
+++ b/gui/src/main/java/io/bitsquare/trade/TradeState.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TradeState {
+ private static final Logger log = LoggerFactory.getLogger(TradeState.class);
+
+ public TradeState() {
+
+ }
+}
diff --git a/gui/src/main/java/io/bitsquare/trade/protocol/createoffer/CreateOfferCoordinator.java b/gui/src/main/java/io/bitsquare/trade/protocol/createoffer/CreateOfferCoordinator.java
deleted file mode 100644
index 9d5f4912b8..0000000000
--- a/gui/src/main/java/io/bitsquare/trade/protocol/createoffer/CreateOfferCoordinator.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * This file is part of Bitsquare.
- *
- * Bitsquare is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or (at
- * your option) any later version.
- *
- * Bitsquare is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
- * License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with Bitsquare. If not, see .
- */
-
-package io.bitsquare.trade.protocol.createoffer;
-
-import io.bitsquare.btc.WalletService;
-import io.bitsquare.offer.Offer;
-import io.bitsquare.offer.OfferRepository;
-import io.bitsquare.trade.handlers.TransactionResultHandler;
-import io.bitsquare.util.task.FaultHandler;
-
-import org.bitcoinj.core.InsufficientMoneyException;
-import org.bitcoinj.core.Transaction;
-
-import com.google.common.util.concurrent.FutureCallback;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Responsible for coordinating tasks involved in the create offer process.
- */
-public class CreateOfferCoordinator {
-
- private static final Logger log = LoggerFactory.getLogger(CreateOfferCoordinator.class);
-
- private final Offer offer;
- private final WalletService walletService;
- private final TransactionResultHandler resultHandler;
- private final FaultHandler faultHandler;
- private final OfferRepository offerRepository;
-
- public CreateOfferCoordinator(Offer offer, WalletService walletService, TransactionResultHandler resultHandler,
- FaultHandler faultHandler, OfferRepository offerRepository) {
- this.offer = offer;
- this.walletService = walletService;
- this.resultHandler = resultHandler;
- this.faultHandler = faultHandler;
- this.offerRepository = offerRepository;
- }
-
- public void start() {
- try {
- offer.validate();
- } catch (Exception ex) {
- faultHandler.handleFault("Offer validation failed", ex);
- return;
- }
-
- Transaction transaction;
-
- try {
- transaction = walletService.createOfferFeeTx(offer.getId());
- offer.setOfferFeePaymentTxID(transaction.getHashAsString());
- } catch (InsufficientMoneyException ex) {
- faultHandler.handleFault(
- "Offer fee payment failed because there is insufficient money in the trade wallet", ex);
- return;
- } catch (Throwable ex) {
- faultHandler.handleFault("Offer fee payment failed because of an exception occurred", ex);
- return;
- }
-
- try {
- walletService.broadcastCreateOfferFeeTx(transaction, new FutureCallback() {
- @Override
- public void onSuccess(Transaction transaction) {
- log.info("sendResult onSuccess:" + transaction);
- if (transaction == null) {
- faultHandler.handleFault("Offer fee payment failed.",
- new Exception("Offer fee payment failed. Transaction = null."));
- return;
- }
- offerRepository.addOffer(offer, () -> resultHandler.onResult(transaction), faultHandler);
- }
-
- @Override
- public void onFailure(Throwable t) {
- faultHandler.handleFault("Offer fee payment failed with an exception.", t);
- }
- });
- } catch (Throwable t) {
- faultHandler.handleFault("Offer fee payment failed because an exception occurred.", t);
- return;
- }
- }
-}
diff --git a/gui/src/main/java/io/bitsquare/trade/protocol/createoffer/CreateOfferProtocol.java b/gui/src/main/java/io/bitsquare/trade/protocol/createoffer/CreateOfferProtocol.java
new file mode 100644
index 0000000000..3145def0d8
--- /dev/null
+++ b/gui/src/main/java/io/bitsquare/trade/protocol/createoffer/CreateOfferProtocol.java
@@ -0,0 +1,146 @@
+/*
+ * 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.createoffer;
+
+import io.bitsquare.btc.WalletService;
+import io.bitsquare.offer.Offer;
+import io.bitsquare.offer.RemoteOfferBook;
+import io.bitsquare.trade.handlers.TransactionResultHandler;
+import io.bitsquare.util.task.FaultHandler;
+
+import org.bitcoinj.core.InsufficientMoneyException;
+import org.bitcoinj.core.Transaction;
+
+import com.google.common.util.concurrent.FutureCallback;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Responsible for coordinating tasks involved in the create offer process.
+ * Executed on UI thread (single threaded)
+ */
+public class CreateOfferProtocol {
+
+ private static final Logger log = LoggerFactory.getLogger(CreateOfferProtocol.class);
+
+ private final Offer offer;
+ private final WalletService walletService;
+ private final TransactionResultHandler resultHandler;
+ private final FaultHandler faultHandler;
+ private final RemoteOfferBook remoteOfferBook;
+ private int repeatAddOfferCallCounter = 0;
+
+ public CreateOfferProtocol(Offer offer, WalletService walletService, TransactionResultHandler resultHandler,
+ FaultHandler faultHandler, RemoteOfferBook remoteOfferBook) {
+ this.offer = offer;
+ this.walletService = walletService;
+ this.resultHandler = resultHandler;
+ this.faultHandler = faultHandler;
+ this.remoteOfferBook = remoteOfferBook;
+ }
+
+ public void createOffer() {
+ try {
+ validateOffer();
+ Transaction transaction = createOfferFeeTx();
+
+ TransactionResultHandler resultHandler1 = transaction1 -> addOffer(transaction1);
+ FaultHandler faultHandler1 = (message, throwable) -> faultHandler.handleFault(message, throwable);
+ broadcastCreateOfferFeeTx(transaction, resultHandler1, faultHandler1);
+
+ } catch (Throwable t) {
+ }
+
+ }
+
+ // 1. Validate offer data
+ // Sync
+ // In case of an error: No rollback activity needed
+ void validateOffer() throws Exception {
+ try {
+ offer.validate();
+ } catch (Exception ex) {
+ faultHandler.handleFault("Offer validation failed", ex);
+ throw ex;
+ }
+ }
+
+ // 2. createOfferFeeTx
+ // Sync
+ // In case of an error: No rollback activity needed
+ Transaction createOfferFeeTx() throws Exception {
+ try {
+ return walletService.createOfferFeeTx(offer.getId());
+ } catch (InsufficientMoneyException ex) {
+ faultHandler.handleFault(
+ "Offer fee payment failed because there is insufficient money in the trade wallet", ex);
+ throw ex;
+ } catch (Throwable t) {
+ faultHandler.handleFault("Offer fee payment failed because of an exception occurred", t);
+ throw t;
+ }
+ }
+
+ // 3. broadcastCreateOfferFeeTx
+ // Async
+ // In case of an error: Not sure if there can be an inconsistent state in failure case. Assuming not but need to check further.
+ void broadcastCreateOfferFeeTx(Transaction transaction, TransactionResultHandler resultHandler1, FaultHandler faultHandler1) throws Exception {
+ try {
+ walletService.broadcastCreateOfferFeeTx(transaction, new FutureCallback() {
+ @Override
+ public void onSuccess(Transaction transaction) {
+ log.info("Broadcast of offer fee payment succeeded: transaction = " + transaction.toString());
+ if (transaction == null) {
+ Exception ex = new Exception("Broadcast of offer fee payment failed because transaction = null.");
+ faultHandler.handleFault("Broadcast of offer fee payment failed.", ex);
+ }
+ resultHandler1.onResult(transaction);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ faultHandler1.handleFault("Broadcast of offer fee payment failed with an exception.", t);
+ }
+ });
+ } catch (Throwable t) {
+ faultHandler1.handleFault("Broadcast of offer fee payment failed with an exception.", t);
+ throw t;
+ }
+ }
+
+ // 4. addOffer
+ // Async
+ // In case of an error: Try again, afterwards give up.
+ void addOffer(Transaction transaction) {
+ remoteOfferBook.addOffer(offer,
+ () -> {
+ offer.setOfferFeePaymentTxID(transaction.getHashAsString());
+ resultHandler.onResult(transaction);
+ },
+ (message, throwable) -> {
+ repeatAddOfferCallCounter++;
+ if (repeatAddOfferCallCounter > 1) {
+ faultHandler.handleFault(message, throwable);
+ }
+ else {
+ addOffer(transaction);
+ }
+ });
+ }
+}
diff --git a/gui/src/main/java/io/bitsquare/util/Bytes.java b/gui/src/main/java/io/bitsquare/util/Bytes.java
new file mode 100644
index 0000000000..fa6d9edbf1
--- /dev/null
+++ b/gui/src/main/java/io/bitsquare/util/Bytes.java
@@ -0,0 +1,68 @@
+/*
+ * 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.util;
+
+import org.bitcoinj.core.Utils;
+
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+import java.io.IOException;
+
+import java.util.Arrays;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Bytes {
+ private static final Logger log = LoggerFactory.getLogger(Bytes.class);
+
+ public final byte[] bytes;
+ public final String string;
+
+ public Bytes(byte[] bytes) {
+ this.bytes = Arrays.copyOf(bytes, bytes.length);
+ this.string = Utils.HEX.encode(bytes);
+ }
+
+ public Bytes(String string) {
+ this.string = string;
+ this.bytes = Utils.HEX.decode(string);
+ }
+
+ @Override
+ public String toString() {
+ return string;
+ }
+
+ public static class GsonAdapter extends TypeAdapter {
+ @Override
+ public Bytes read(JsonReader reader) throws IOException {
+ return new Bytes(reader.nextString());
+ }
+
+ @Override
+ public void write(JsonWriter out, Bytes value) throws IOException {
+ if (value == null)
+ out.nullValue();
+ else
+ out.value(value.string);
+ }
+ }
+}
diff --git a/gui/src/main/java/io/bitsquare/util/Utilities.java b/gui/src/main/java/io/bitsquare/util/Utilities.java
index 8c3ddfd277..b784e2d642 100644
--- a/gui/src/main/java/io/bitsquare/util/Utilities.java
+++ b/gui/src/main/java/io/bitsquare/util/Utilities.java
@@ -25,6 +25,7 @@ import java.awt.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
@@ -177,6 +178,30 @@ public class Utilities {
return obj;
}
+ /**
+ * Empty and delete a folder (and subfolders).
+ * @param folder
+ * folder to empty
+ */
+ public static void removeDirectory(final File folder) {
+ // check if folder file is a real folder
+ if (folder.isDirectory()) {
+ File[] list = folder.listFiles();
+ if (list != null) {
+ for (int i = 0; i < list.length; i++) {
+ File tmpF = list[i];
+ if (tmpF.isDirectory()) {
+ removeDirectory(tmpF);
+ }
+ tmpF.delete();
+ }
+ }
+ if (!folder.delete()) {
+ log.warn("can't delete folder : " + folder);
+ }
+ }
+ }
+
public static AnimationTimer setTimeout(int delay, Function callback) {
AnimationTimer animationTimer = new AnimationTimer() {
final long lastTimeStamp = System.currentTimeMillis();
diff --git a/gui/src/main/resources/logback.xml b/gui/src/main/resources/logback.xml
index bf5c415ea0..1d2e3cd137 100644
--- a/gui/src/main/resources/logback.xml
+++ b/gui/src/main/resources/logback.xml
@@ -26,9 +26,9 @@
-
-
-
+
+
+
diff --git a/gui/src/test/java/io/bitsquare/msg/TomP2PTests.java b/gui/src/test/java/io/bitsquare/msg/TomP2PTests.java
index f138fc73ea..2021dea704 100644
--- a/gui/src/test/java/io/bitsquare/msg/TomP2PTests.java
+++ b/gui/src/test/java/io/bitsquare/msg/TomP2PTests.java
@@ -74,7 +74,7 @@ import static org.junit.Assert.*;
* Test bootstrapping, DHT operations like put/get/add/remove and sendDirect in both LAN and WAN environment
* Test scenarios in direct connection, auto port forwarding or relay mode.
*
- * To start a bootstrap node code use the {@link io.bitsquare.app.cli.BootstrapNode} class.
+ * To start a bootstrap node code use the {@link io.bitsquare.app.bootstrap.BootstrapNode} class.
*
* To configure your test environment edit the static fields for id, IP and port.
* In the configure method and the connectionType you can define your test scenario.
diff --git a/gui/src/test/java/io/bitsquare/trade/protocol/createoffer/CreateOfferProtocolTest.java b/gui/src/test/java/io/bitsquare/trade/protocol/createoffer/CreateOfferProtocolTest.java
new file mode 100644
index 0000000000..6fb09fb6e6
--- /dev/null
+++ b/gui/src/test/java/io/bitsquare/trade/protocol/createoffer/CreateOfferProtocolTest.java
@@ -0,0 +1,318 @@
+/*
+ * 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.createoffer;
+
+import io.bitsquare.arbitrator.Arbitrator;
+import io.bitsquare.bank.BankAccountType;
+import io.bitsquare.btc.BitcoinNetwork;
+import io.bitsquare.btc.FeePolicy;
+import io.bitsquare.btc.UserAgent;
+import io.bitsquare.btc.WalletService;
+import io.bitsquare.locale.CountryUtil;
+import io.bitsquare.locale.LanguageUtil;
+import io.bitsquare.msg.tomp2p.BootstrappedPeerBuilder;
+import io.bitsquare.msg.tomp2p.TomP2PMessageService;
+import io.bitsquare.msg.tomp2p.TomP2PNode;
+import io.bitsquare.network.BootstrapState;
+import io.bitsquare.network.Node;
+import io.bitsquare.offer.Direction;
+import io.bitsquare.offer.Offer;
+import io.bitsquare.offer.RemoteOfferBook;
+import io.bitsquare.offer.tomp2p.TomP2POfferBook;
+import io.bitsquare.persistence.Persistence;
+import io.bitsquare.trade.handlers.TransactionResultHandler;
+import io.bitsquare.user.User;
+import io.bitsquare.util.DSAKeyUtil;
+import io.bitsquare.util.task.FaultHandler;
+
+import org.bitcoinj.core.Address;
+import org.bitcoinj.core.Coin;
+import org.bitcoinj.core.Transaction;
+import org.bitcoinj.utils.Threading;
+
+import java.io.File;
+import java.io.IOException;
+
+import java.util.Arrays;
+import java.util.Currency;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import rx.Observable;
+
+import static org.junit.Assert.*;
+
+/**
+ * That test is ignored for automated testing as it needs custom setup.
+ *
+ * It uses RegTest mode of Bitcoin network and localhost TomP2P network.
+ *
+ * 1. Need a first run to get the wallet receiving address.
+ * 2. Fund that from regtest Bitcoin Core client.
+ * 3. Create a block on regtest Bitcoin Core (setgenerate true) to get the balance.
+ * 4. Start BootstrapNodeMain at localhost with program args: --node.name localhost
+ */
+@Ignore
+public class CreateOfferProtocolTest {
+ private static final Logger log = LoggerFactory.getLogger(CreateOfferProtocolTest.class);
+
+ private WalletService walletService;
+ private TomP2PMessageService messageService;
+ private RemoteOfferBook remoteOfferBook;
+ private final File dir = new File("./temp");
+ private final static String OFFER_ID = "offerID";
+ private Address address;
+
+ @Before
+ public void setup() throws InterruptedException {
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ dir.mkdirs();
+
+ Persistence persistence = new Persistence(dir, "prefs");
+ persistence.init();
+
+ // messageService
+ Node bootstrapNode = Node.at("localhost", "127.0.0.1");
+ User user = new User();
+ user.applyPersistedUser(null);
+ BootstrappedPeerBuilder bootstrappedPeerBuilder = new BootstrappedPeerBuilder(Node.DEFAULT_PORT, false, bootstrapNode, "");
+ TomP2PNode p2pNode = new TomP2PNode(bootstrappedPeerBuilder);
+ messageService = new TomP2PMessageService(user, p2pNode);
+
+ Observable messageObservable = messageService.init();
+ messageObservable.publish();
+ messageObservable.subscribe(
+ state -> log.trace("state changed: " + state),
+ error -> {
+ log.error(error.toString());
+ },
+ () -> {
+ log.trace("message completed");
+
+ remoteOfferBook = new TomP2POfferBook(p2pNode);
+ remoteOfferBook.setExecutor(Threading.SAME_THREAD);
+ }
+ );
+ bootstrappedPeerBuilder.start();
+
+ // WalletService
+ walletService = new WalletService(BitcoinNetwork.REGTEST,
+ new FeePolicy(BitcoinNetwork.REGTEST),
+ null,
+ persistence,
+ new UserAgent("", ""),
+ dir,
+ "Tests"
+ );
+
+ Observable