Add CreateOfferProtocolTests

This commit is contained in:
Manfred Karrer 2015-03-07 23:46:57 +01:00
parent 86ec3380b2
commit d832cfe74b
26 changed files with 721 additions and 172 deletions

View file

@ -15,7 +15,7 @@
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>. * along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/ */
package io.bitsquare.app.cli; package io.bitsquare.app.bootstrap;
import io.bitsquare.network.Node; import io.bitsquare.network.Node;

View file

@ -15,7 +15,7 @@
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>. * along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/ */
package io.bitsquare.app.cli; package io.bitsquare.app.bootstrap;
import io.bitsquare.app.BitsquareEnvironment; import io.bitsquare.app.BitsquareEnvironment;
import io.bitsquare.app.BitsquareExecutable; import io.bitsquare.app.BitsquareExecutable;

View file

@ -58,6 +58,7 @@ public class BitsquareAppMain extends BitsquareExecutable {
// We don't want to do the full argument parsing here as that might easily change in update versions // We don't want to do the full argument parsing here as that might easily change in update versions
// So we only handle the absolute minimum which is APP_NAME, APP_DATA_DIR_KEY and USER_DATA_DIR // So we only handle the absolute minimum which is APP_NAME, APP_DATA_DIR_KEY and USER_DATA_DIR
OptionParser parser = new OptionParser(); OptionParser parser = new OptionParser();
parser.allowsUnrecognizedOptions();
parser.accepts(USER_DATA_DIR_KEY, description("User data directory", DEFAULT_USER_DATA_DIR)) parser.accepts(USER_DATA_DIR_KEY, description("User data directory", DEFAULT_USER_DATA_DIR))
.withRequiredArg(); .withRequiredArg();
parser.accepts(APP_NAME_KEY, description("Application name", DEFAULT_APP_NAME)) parser.accepts(APP_NAME_KEY, description("Application name", DEFAULT_APP_NAME))

View file

@ -24,6 +24,8 @@ import org.bitcoinj.crypto.DeterministicKey;
import java.io.Serializable; import java.io.Serializable;
import java.util.Arrays;
/** /**
* Is a minimalistic wallet abstraction used to separate transactions between different activities like: * Is a minimalistic wallet abstraction used to separate transactions between different activities like:
* Registration, trade and arbiter deposit. * Registration, trade and arbiter deposit.
@ -87,4 +89,16 @@ public class AddressEntry implements Serializable {
TRADE, TRADE,
ARBITRATOR_DEPOSIT ARBITRATOR_DEPOSIT
} }
@Override
public String toString() {
return "AddressEntry{" +
"addressString=" + getAddress().toString() +
"key=" + key +
", params=" + params +
", addressContext=" + addressContext +
", offerId='" + offerId + '\'' +
", pubKeyHash=" + Arrays.toString(pubKeyHash) +
'}';
}
} }

View file

@ -55,8 +55,8 @@ public class FeePolicy {
takeOfferFeeAddress = "1BVxNn3T12veSK6DgqwU4Hdn7QHcDDRag7"; takeOfferFeeAddress = "1BVxNn3T12veSK6DgqwU4Hdn7QHcDDRag7";
break; break;
case REGTEST: case REGTEST:
createOfferFeeAddress = "n2upbsaKAe4PD3cc4JfS7UCqPC5oNd7Ckg"; createOfferFeeAddress = "mmdXHjPSmLCAShckfQ1jwnLYpbP2pKKF7y";
takeOfferFeeAddress = "n2upbsaKAe4PD3cc4JfS7UCqPC5oNd7Ckg"; takeOfferFeeAddress = "mmdXHjPSmLCAShckfQ1jwnLYpbP2pKKF7y";
break; break;
default: default:
throw new BitsquareException("Unknown bitcoin network: %s", bitcoinNetwork); throw new BitsquareException("Unknown bitcoin network: %s", bitcoinNetwork);

View file

@ -534,8 +534,9 @@ public class WalletService {
sendRequest.shuffleOutputs = false; sendRequest.shuffleOutputs = false;
// we allow spending of unconfirmed tx (double spend risk is low and usability would suffer if we need to // we allow spending of unconfirmed tx (double spend risk is low and usability would suffer if we need to
// wait for 1 confirmation) // wait for 1 confirmation)
sendRequest.coinSelector = new AddressBasedCoinSelector(params, getAddressInfoByTradeID(offerId), true); AddressEntry addressEntry = getAddressInfoByTradeID(offerId);
sendRequest.changeAddress = getAddressInfoByTradeID(offerId).getAddress(); sendRequest.coinSelector = new AddressBasedCoinSelector(params, addressEntry, true);
sendRequest.changeAddress = addressEntry.getAddress();
wallet.completeTx(sendRequest); wallet.completeTx(sendRequest);
printInputs("payCreateOfferFee", tx); printInputs("payCreateOfferFee", tx);
return tx; return tx;

View file

@ -160,9 +160,9 @@ class MainViewModel implements ViewModel {
error -> log.error(error.toString()), error -> log.error(error.toString()),
() -> Platform.runLater(() -> setBitcoinNetworkSyncProgress(1.0))); () -> Platform.runLater(() -> setBitcoinNetworkSyncProgress(1.0)));
Observable<BootstrapState> message = messageService.init(); Observable<BootstrapState> messageObservable = messageService.init();
message.publish(); messageObservable.publish();
message.subscribe( messageObservable.subscribe(
state -> Platform.runLater(() -> setBootstrapState(state)), state -> Platform.runLater(() -> setBootstrapState(state)),
error -> Platform.runLater(() -> { error -> Platform.runLater(() -> {
log.error(error.toString()); log.error(error.toString());
@ -173,8 +173,8 @@ class MainViewModel implements ViewModel {
}), }),
() -> log.trace("message completed")); () -> log.trace("message completed"));
Observable<Object> wallet = walletService.initialize(Platform::runLater); Observable<Object> walletServiceObservable = walletService.initialize(Platform::runLater);
wallet.subscribe( walletServiceObservable.subscribe(
next -> { next -> {
log.trace("wallet next"); log.trace("wallet next");
}, },
@ -186,8 +186,8 @@ class MainViewModel implements ViewModel {
log.trace("wallet completed"); log.trace("wallet completed");
}); });
Observable<UpdateProcess.State> updateProcess = this.updateProcess.getProcess(); Observable<UpdateProcess.State> updateProcessObservable = this.updateProcess.getProcess();
updateProcess.subscribe(next -> { updateProcessObservable.subscribe(next -> {
log.trace("updateProcess next"); log.trace("updateProcess next");
}, },
error -> { error -> {
@ -197,7 +197,7 @@ class MainViewModel implements ViewModel {
log.trace("updateProcess completed"); log.trace("updateProcess completed");
}); });
Observable<?> allTasks = Observable.merge(message, wallet, updateProcess); Observable<?> allTasks = Observable.merge(messageObservable, walletServiceObservable, updateProcessObservable);
allTasks.subscribe( allTasks.subscribe(
next -> { next -> {
}, },

View file

@ -21,7 +21,7 @@ import io.bitsquare.bank.BankAccount;
import io.bitsquare.locale.Country; import io.bitsquare.locale.Country;
import io.bitsquare.locale.CurrencyUtil; import io.bitsquare.locale.CurrencyUtil;
import io.bitsquare.offer.Offer; import io.bitsquare.offer.Offer;
import io.bitsquare.offer.OfferRepository; import io.bitsquare.offer.RemoteOfferBook;
import io.bitsquare.user.User; import io.bitsquare.user.User;
import io.bitsquare.util.Utilities; import io.bitsquare.util.Utilities;
@ -50,11 +50,11 @@ public class OfferBook {
private static final Logger log = LoggerFactory.getLogger(OfferBook.class); private static final Logger log = LoggerFactory.getLogger(OfferBook.class);
private final OfferRepository offerRepository; private final RemoteOfferBook remoteOfferBook;
private final User user; private final User user;
private final ObservableList<OfferBookListItem> offerBookListItems = FXCollections.observableArrayList(); private final ObservableList<OfferBookListItem> offerBookListItems = FXCollections.observableArrayList();
private final OfferRepository.Listener offerRepositoryListener; private final RemoteOfferBook.Listener remoteOfferBookListener;
private final ChangeListener<BankAccount> bankAccountChangeListener; private final ChangeListener<BankAccount> bankAccountChangeListener;
private final ChangeListener<Number> invalidationListener; private final ChangeListener<Number> invalidationListener;
private String fiatCode; private String fiatCode;
@ -68,14 +68,14 @@ public class OfferBook {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@Inject @Inject
OfferBook(OfferRepository offerRepository, User user) { OfferBook(RemoteOfferBook remoteOfferBook, User user) {
this.offerRepository = offerRepository; this.remoteOfferBook = remoteOfferBook;
this.user = user; this.user = user;
bankAccountChangeListener = (observableValue, oldValue, newValue) -> setBankAccount(newValue); bankAccountChangeListener = (observableValue, oldValue, newValue) -> setBankAccount(newValue);
invalidationListener = (ov, oldValue, newValue) -> requestOffers(); invalidationListener = (ov, oldValue, newValue) -> requestOffers();
offerRepositoryListener = new OfferRepository.Listener() { remoteOfferBookListener = new RemoteOfferBook.Listener() {
@Override @Override
public void onOfferAdded(Offer offer) { public void onOfferAdded(Offer offer) {
addOfferToOfferBookListItems(offer); addOfferToOfferBookListItems(offer);
@ -142,15 +142,15 @@ public class OfferBook {
private void addListeners() { private void addListeners() {
log.debug("addListeners "); log.debug("addListeners ");
user.currentBankAccountProperty().addListener(bankAccountChangeListener); user.currentBankAccountProperty().addListener(bankAccountChangeListener);
offerRepository.addListener(offerRepositoryListener); remoteOfferBook.addListener(remoteOfferBookListener);
offerRepository.invalidationTimestampProperty().addListener(invalidationListener); remoteOfferBook.invalidationTimestampProperty().addListener(invalidationListener);
} }
private void removeListeners() { private void removeListeners() {
log.debug("removeListeners "); log.debug("removeListeners ");
user.currentBankAccountProperty().removeListener(bankAccountChangeListener); user.currentBankAccountProperty().removeListener(bankAccountChangeListener);
offerRepository.removeListener(offerRepositoryListener); remoteOfferBook.removeListener(remoteOfferBookListener);
offerRepository.invalidationTimestampProperty().removeListener(invalidationListener); remoteOfferBook.invalidationTimestampProperty().removeListener(invalidationListener);
} }
private void addOfferToOfferBookListItems(Offer offer) { private void addOfferToOfferBookListItems(Offer offer) {
@ -160,7 +160,7 @@ public class OfferBook {
} }
private void requestOffers() { private void requestOffers() {
offerRepository.getOffers(fiatCode); remoteOfferBook.getOffers(fiatCode);
} }
@ -173,11 +173,11 @@ public class OfferBook {
addListeners(); addListeners();
setBankAccount(user.getCurrentBankAccount().get()); setBankAccount(user.getCurrentBankAccount().get());
pollingTimer = Utilities.setInterval(3000, (animationTimer) -> { pollingTimer = Utilities.setInterval(3000, (animationTimer) -> {
offerRepository.requestInvalidationTimeStampFromDHT(fiatCode); remoteOfferBook.requestInvalidationTimeStampFromDHT(fiatCode);
return null; return null;
}); });
offerRepository.getOffers(fiatCode); remoteOfferBook.getOffers(fiatCode);
} }
private void stopPolling() { private void stopPolling() {

View file

@ -68,7 +68,7 @@ import io.netty.util.concurrent.DefaultEventExecutorGroup;
/** /**
* Creates a DHT peer and bootstraps to the network via a bootstrap node * 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); private static final Logger log = LoggerFactory.getLogger(BootstrappedPeerBuilder.class);
static final String BOOTSTRAP_NODE_KEY = "bootstrapNode"; static final String BOOTSTRAP_NODE_KEY = "bootstrapNode";

View file

@ -66,7 +66,7 @@ import rx.Observable;
* <p> * <p>
* TODO: improve callbacks that Platform.runLater is not necessary. We call usually that methods form teh UI thread. * 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 Logger log = LoggerFactory.getLogger(TomP2PMessageService.class);
private static final String ARBITRATORS_ROOT = "ArbitratorsRoot"; private static final String ARBITRATORS_ROOT = "ArbitratorsRoot";

View file

@ -30,7 +30,7 @@ public interface BootstrapNodes {
Node DEFAULT = DIGITAL_OCEAN_1; 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()}. * Typically used only for testing. Not included in results from {@link #all()}.
*/ */
Node LOCALHOST = Node.at("localhost", "127.0.0.1"); Node LOCALHOST = Node.at("localhost", "127.0.0.1");

View file

@ -27,10 +27,4 @@ public abstract class OfferModule extends BitsquareModule {
super(env); super(env);
} }
@Override
protected void configure() {
bind(OfferRepository.class).to(offerRepository()).asEagerSingleton();
}
protected abstract Class<? extends OfferRepository> offerRepository();
} }

View file

@ -21,10 +21,13 @@ import io.bitsquare.util.task.FaultHandler;
import io.bitsquare.util.task.ResultHandler; import io.bitsquare.util.task.ResultHandler;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor;
import javafx.beans.property.LongProperty; import javafx.beans.property.LongProperty;
public interface OfferRepository { public interface RemoteOfferBook {
void setExecutor(Executor executor);
void getOffers(String fiatCode); void getOffers(String fiatCode);

View file

@ -19,7 +19,7 @@ package io.bitsquare.offer.tomp2p;
import io.bitsquare.msg.tomp2p.TomP2PNode; import io.bitsquare.msg.tomp2p.TomP2PNode;
import io.bitsquare.offer.Offer; 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.FaultHandler;
import io.bitsquare.util.task.ResultHandler; import io.bitsquare.util.task.ResultHandler;
@ -28,10 +28,10 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Executor;
import javax.inject.Inject; import javax.inject.Inject;
import javafx.application.Platform;
import javafx.beans.property.LongProperty; import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty; import javafx.beans.property.SimpleLongProperty;
@ -48,20 +48,25 @@ import net.tomp2p.storage.Data;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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<Listener> offerRepositoryListeners = new ArrayList<>(); private final List<Listener> offerRepositoryListeners = new ArrayList<>();
private final LongProperty invalidationTimestamp = new SimpleLongProperty(0); private final LongProperty invalidationTimestamp = new SimpleLongProperty(0);
private final TomP2PNode p2pNode; private final TomP2PNode p2pNode;
private Executor executor;
@Inject @Inject
public TomP2POfferRepository(TomP2PNode p2pNode) { public TomP2POfferBook(TomP2PNode p2pNode) {
this.p2pNode = p2pNode; this.p2pNode = p2pNode;
} }
public void setExecutor(Executor executor) {
this.executor = executor;
}
@Override @Override
public void addOffer(Offer offer, ResultHandler resultHandler, FaultHandler faultHandler) { public void addOffer(Offer offer, ResultHandler resultHandler, FaultHandler faultHandler) {
Number160 locationKey = Number160.createHash(offer.getCurrency().getCurrencyCode()); Number160 locationKey = Number160.createHash(offer.getCurrency().getCurrencyCode());
@ -78,13 +83,13 @@ class TomP2POfferRepository implements OfferRepository {
@Override @Override
public void operationComplete(BaseFuture future) throws Exception { public void operationComplete(BaseFuture future) throws Exception {
if (future.isSuccess()) { if (future.isSuccess()) {
Platform.runLater(() -> { executor.execute(() -> {
resultHandler.handleResult(); resultHandler.handleResult();
offerRepositoryListeners.stream().forEach(listener -> { offerRepositoryListeners.stream().forEach(listener -> {
try { try {
Object offerDataObject = offerData.object(); Object offerDataObject = offerData.object();
if (offerDataObject instanceof Offer) { 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); listener.onOfferAdded((Offer) offerDataObject);
} }
} catch (ClassNotFoundException | IOException e) { } catch (ClassNotFoundException | IOException e) {
@ -102,15 +107,11 @@ class TomP2POfferRepository implements OfferRepository {
@Override @Override
public void exceptionCaught(Throwable ex) throws Exception { public void exceptionCaught(Throwable ex) throws Exception {
Platform.runLater(() -> { executor.execute(() -> faultHandler.handleFault("Failed to add offer to DHT", ex));
faultHandler.handleFault("Failed to add offer to DHT", ex);
});
} }
}); });
} catch (IOException ex) { } catch (IOException ex) {
Platform.runLater(() -> { executor.execute(() -> faultHandler.handleFault("Failed to add offer to DHT", ex));
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 // it might change in future to something like foundAndRemoved and notFound
// See discussion at: https://github.com/tomp2p/TomP2P/issues/57#issuecomment-62069840 // See discussion at: https://github.com/tomp2p/TomP2P/issues/57#issuecomment-62069840
Platform.runLater(() -> { executor.execute(() -> {
offerRepositoryListeners.stream().forEach(listener -> { offerRepositoryListeners.stream().forEach(listener -> {
try { try {
Object offerDataObject = offerData.object(); 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))); listener.onOffersReceived(offers)));
} }
@ -193,7 +194,7 @@ class TomP2POfferRepository implements OfferRepository {
final Map<Number640, Data> dataMap = futureGet.dataMap(); final Map<Number640, Data> dataMap = futureGet.dataMap();
if (dataMap == null || dataMap.size() == 0) { if (dataMap == null || dataMap.size() == 0) {
log.trace("Get offers from DHT delivered empty dataMap."); 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<>()))); listener.onOffersReceived(new ArrayList<>())));
} }
else { else {
@ -262,7 +263,7 @@ class TomP2POfferRepository implements OfferRepository {
Data data = futureGet.data(); Data data = futureGet.data();
if (data != null && data.object() instanceof Long) { if (data != null && data.object() instanceof Long) {
final Object object = data.object(); final Object object = data.object();
Platform.runLater(() -> { executor.execute(() -> {
Long timeStamp = (Long) object; Long timeStamp = (Long) object;
//log.trace("Get invalidationTimestamp from DHT was successful. TimeStamp=" + timeStamp); //log.trace("Get invalidationTimestamp from DHT was successful. TimeStamp=" + timeStamp);
invalidationTimestamp.set(timeStamp); invalidationTimestamp.set(timeStamp);

View file

@ -17,8 +17,15 @@
package io.bitsquare.offer.tomp2p; package io.bitsquare.offer.tomp2p;
import io.bitsquare.msg.tomp2p.TomP2PNode;
import io.bitsquare.offer.OfferModule; 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; import org.springframework.core.env.Environment;
@ -29,7 +36,21 @@ public class TomP2POfferModule extends OfferModule {
} }
@Override @Override
public Class<? extends OfferRepository> offerRepository() { protected void configure() {
return TomP2POfferRepository.class; bind(RemoteOfferBook.class).toProvider(RemoteOfferBookProvider.class).asEagerSingleton();
}
}
class RemoteOfferBookProvider implements Provider<RemoteOfferBook> {
private final TomP2POfferBook remoteOfferBook;
@Inject
public RemoteOfferBookProvider(TomP2PNode p2pNode) {
remoteOfferBook = new TomP2POfferBook(p2pNode);
remoteOfferBook.setExecutor(Platform::runLater);
}
public RemoteOfferBook get() {
return remoteOfferBook;
} }
} }

View file

@ -28,10 +28,10 @@ import io.bitsquare.msg.listeners.OutgoingMessageListener;
import io.bitsquare.network.Peer; import io.bitsquare.network.Peer;
import io.bitsquare.offer.Direction; import io.bitsquare.offer.Direction;
import io.bitsquare.offer.Offer; import io.bitsquare.offer.Offer;
import io.bitsquare.offer.OfferRepository; import io.bitsquare.offer.RemoteOfferBook;
import io.bitsquare.persistence.Persistence; import io.bitsquare.persistence.Persistence;
import io.bitsquare.trade.handlers.TransactionResultHandler; 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.TradeMessage;
import io.bitsquare.trade.protocol.trade.offerer.BuyerAcceptsOfferProtocol; import io.bitsquare.trade.protocol.trade.offerer.BuyerAcceptsOfferProtocol;
import io.bitsquare.trade.protocol.trade.offerer.BuyerAcceptsOfferProtocolListener; import io.bitsquare.trade.protocol.trade.offerer.BuyerAcceptsOfferProtocolListener;
@ -83,7 +83,7 @@ public class TradeManager {
private final BlockChainService blockChainService; private final BlockChainService blockChainService;
private final WalletService walletService; private final WalletService walletService;
private final SignatureService signatureService; private final SignatureService signatureService;
private final OfferRepository offerRepository; private final RemoteOfferBook remoteOfferBook;
//TODO store TakerAsSellerProtocol in trade //TODO store TakerAsSellerProtocol in trade
private final Map<String, SellerTakesOfferProtocol> takerAsSellerProtocolMap = new HashMap<>(); private final Map<String, SellerTakesOfferProtocol> takerAsSellerProtocolMap = new HashMap<>();
@ -106,7 +106,7 @@ public class TradeManager {
public TradeManager(User user, AccountSettings accountSettings, Persistence persistence, public TradeManager(User user, AccountSettings accountSettings, Persistence persistence,
MessageService messageService, BlockChainService blockChainService, MessageService messageService, BlockChainService blockChainService,
WalletService walletService, SignatureService signatureService, WalletService walletService, SignatureService signatureService,
OfferRepository offerRepository) { RemoteOfferBook remoteOfferBook) {
this.user = user; this.user = user;
this.accountSettings = accountSettings; this.accountSettings = accountSettings;
this.persistence = persistence; this.persistence = persistence;
@ -114,7 +114,7 @@ public class TradeManager {
this.blockChainService = blockChainService; this.blockChainService = blockChainService;
this.walletService = walletService; this.walletService = walletService;
this.signatureService = signatureService; this.signatureService = signatureService;
this.offerRepository = offerRepository; this.remoteOfferBook = remoteOfferBook;
Object offersObject = persistence.read(this, "offers"); Object offersObject = persistence.read(this, "offers");
if (offersObject instanceof Map) { if (offersObject instanceof Map) {
@ -172,7 +172,7 @@ public class TradeManager {
accountSettings.getAcceptedCountries(), accountSettings.getAcceptedCountries(),
accountSettings.getAcceptedLanguageLocales()); accountSettings.getAcceptedLanguageLocales());
CreateOfferCoordinator createOfferCoordinator = new CreateOfferCoordinator( CreateOfferProtocol createOfferCoordinator = new CreateOfferProtocol(
offer, offer,
walletService, walletService,
(transactionId) -> { (transactionId) -> {
@ -186,9 +186,9 @@ public class TradeManager {
} }
}, },
(message, throwable) -> errorMessageHandler.handleErrorMessage(message), (message, throwable) -> errorMessageHandler.handleErrorMessage(message),
offerRepository); remoteOfferBook);
createOfferCoordinator.start(); createOfferCoordinator.createOffer();
} }
private void addOffer(Offer offer) { private void addOffer(Offer offer) {
@ -206,7 +206,7 @@ public class TradeManager {
offers.remove(offer.getId()); offers.remove(offer.getId());
persistOffers(); persistOffers();
offerRepository.removeOffer(offer); remoteOfferBook.removeOffer(offer);
} }

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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() {
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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() {
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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<Transaction>() {
@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;
}
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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<Transaction>() {
@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);
}
});
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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<Bytes> {
@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);
}
}
}

View file

@ -25,6 +25,7 @@ import java.awt.*;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
@ -177,6 +178,30 @@ public class Utilities {
return obj; 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<AnimationTimer, Void> callback) { public static AnimationTimer setTimeout(int delay, Function<AnimationTimer, Void> callback) {
AnimationTimer animationTimer = new AnimationTimer() { AnimationTimer animationTimer = new AnimationTimer() {
final long lastTimeStamp = System.currentTimeMillis(); final long lastTimeStamp = System.currentTimeMillis();

View file

@ -26,9 +26,9 @@
<logger name="io.bitsquare" level="TRACE"/> <logger name="io.bitsquare" level="TRACE"/>
<logger name="org.bitcoinj" level="INFO"/> <logger name="org.bitcoinj" level="WARN"/>
<logger name="net.tomp2p" level="INFO"/> <logger name="net.tomp2p" level="WARN"/>
<logger name="com.vinumeris.updatefx" level="TRACE"/> <logger name="com.vinumeris.updatefx" level="INFO"/>
<logger name="net.tomp2p.message.Encoder" level="WARN"/> <logger name="net.tomp2p.message.Encoder" level="WARN"/>

View file

@ -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 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. * Test scenarios in direct connection, auto port forwarding or relay mode.
* <p> * <p>
* 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.
* <p> * <p>
* To configure your test environment edit the static fields for id, IP and port. * 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. * In the configure method and the connectionType you can define your test scenario.

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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.
* <p/>
* It uses RegTest mode of Bitcoin network and localhost TomP2P network.
* <p/>
* 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, "<unspecified>");
TomP2PNode p2pNode = new TomP2PNode(bootstrappedPeerBuilder);
messageService = new TomP2PMessageService(user, p2pNode);
Observable<BootstrapState> 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<Object> walletServiceObservable = walletService.initialize(Threading.SAME_THREAD);
walletServiceObservable.subscribe(
next -> {
// log.trace("wallet next");
},
error -> {
log.trace("wallet error");
},
() -> {
log.trace("wallet complete");
});
Observable<?> allTasks = Observable.merge(messageObservable, walletServiceObservable);
allTasks.subscribe(
next -> {
//log.trace("next");
},
error -> log.error(error.toString()),
() -> {
log.trace("wallet completed");
// 1. Use that address for funding the trading wallet
address = walletService.getAddressInfoByTradeID(OFFER_ID).getAddress();
log.info("address for funding wallet = " + address.toString());//muoTvFHJmQwPKYoA8Fr7t87UCSfZM4fciG
log.info("Balance = " + walletService.getBalanceForAddress(address));
countDownLatch.countDown();
});
countDownLatch.await();
}
@After
public void shutDown() throws IOException, InterruptedException {
walletService.shutDown();
messageService.shutDown();
}
@Test
public void validateOfferTest() throws InterruptedException {
try {
Offer offer = getOffer();
getCreateOfferCoordinator(offer).validateOffer();
assertTrue(true);
} catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
}
}
@Test
public void createOfferFeeTxTest() throws InterruptedException {
try {
Offer offer = getOffer();
Transaction transaction = getCreateOfferCoordinator(offer).createOfferFeeTx();
assertNotNull(transaction);
} catch (Exception e) {
log.info("address for funding wallet = " + address.toString());
log.info("Balance = " + walletService.getBalanceForAddress(address));
e.printStackTrace();
fail(e.getMessage());
}
}
@Test
public void broadcastCreateOfferFeeTxTest() throws InterruptedException {
try {
log.info("Balance pre = " + walletService.getBalanceForAddress(address));
Offer offer = getOffer();
TransactionResultHandler resultHandler = transaction -> assertNotNull(transaction);
FaultHandler faultHandler = (message, throwable) -> {
log.error(message);
throwable.printStackTrace();
fail(throwable.getMessage());
};
CreateOfferProtocol createOfferCoordinator = getCreateOfferCoordinator(offer);
Transaction transaction = createOfferCoordinator.createOfferFeeTx();
createOfferCoordinator.broadcastCreateOfferFeeTx(transaction, resultHandler, faultHandler);
log.info("Balance post = " + walletService.getBalanceForAddress(address));
} catch (Exception e) {
log.info("address for funding wallet = " + address.toString());
log.info("Balance = " + walletService.getBalanceForAddress(address));
e.printStackTrace();
fail(e.getMessage());
}
}
@Test
public void addOfferTest() throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(2);
try {
Offer offer = getOffer();
remoteOfferBook.addListener(new RemoteOfferBook.Listener() {
@Override
public void onOfferAdded(Offer offer1) {
assertEquals("Offer matching", offer.getId(), offer1.getId());
countDownLatch.countDown();
}
@Override
public void onOffersReceived(List<Offer> offers) {
}
@Override
public void onOfferRemoved(Offer offer) {
}
});
TransactionResultHandler resultHandler = transaction -> {
assertNotNull(transaction);
countDownLatch.countDown();
};
FaultHandler faultHandler = (message, throwable) -> {
log.error(message);
throwable.printStackTrace();
fail(throwable.getMessage());
countDownLatch.countDown();
countDownLatch.countDown();
};
CreateOfferProtocol createOfferCoordinator = getCreateOfferCoordinator(offer, resultHandler, faultHandler);
Transaction transaction = createOfferCoordinator.createOfferFeeTx();
createOfferCoordinator.addOffer(transaction);
countDownLatch.await();
log.info("Finished");
} catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
countDownLatch.countDown();
countDownLatch.countDown();
}
}
@Test
public void createOfferTest() throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(1);
TransactionResultHandler resultHandler = transaction -> {
assertNotNull(transaction);
countDownLatch.countDown();
};
FaultHandler faultHandler = (message, throwable) -> {
log.error(message);
throwable.printStackTrace();
fail(throwable.getMessage());
countDownLatch.countDown();
countDownLatch.countDown();
};
CreateOfferProtocol createOfferCoordinator = getCreateOfferCoordinator(getOffer(), resultHandler, faultHandler);
createOfferCoordinator.createOffer();
countDownLatch.await();
}
private CreateOfferProtocol getCreateOfferCoordinator(Offer offer) throws InterruptedException {
TransactionResultHandler resultHandler = transaction -> log.debug("result transaction=" + transaction.toString());
FaultHandler faultHandler = (message, throwable) -> {
log.error(message);
throwable.printStackTrace();
log.info("Balance = " + walletService.getBalanceForAddress(walletService.getAddressInfoByTradeID(OFFER_ID).getAddress()));
};
return getCreateOfferCoordinator(offer, resultHandler, faultHandler);
}
private CreateOfferProtocol getCreateOfferCoordinator(Offer offer, TransactionResultHandler resultHandler, FaultHandler faultHandler) throws
InterruptedException {
return new CreateOfferProtocol(offer,
walletService,
resultHandler,
faultHandler,
remoteOfferBook);
}
private Offer getOffer() {
return new Offer(OFFER_ID,
DSAKeyUtil.generateKeyPair().getPublic(),
Direction.BUY,
100L,
Coin.CENT,
Coin.CENT,
BankAccountType.INTERNATIONAL,
Currency.getInstance("EUR"),
CountryUtil.getDefaultCountry(),
"bankAccountUID",
Arrays.asList(new Arbitrator()),
Coin.CENT,
Arrays.asList(CountryUtil.getDefaultCountry()),
Arrays.asList(LanguageUtil.getDefaultLanguageLocale())
);
}
}

View file

@ -41,7 +41,7 @@
<minimizeJar>false</minimizeJar> <minimizeJar>false</minimizeJar>
<transformers> <transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>io.bitsquare.app.cli.BootstrapNodeMain</mainClass> <mainClass>io.bitsquare.app.bootstrap.BootstrapNodeMain</mainClass>
</transformer> </transformer>
</transformers> </transformers>
<filters> <filters>