Separate open offer

This commit is contained in:
Manfred Karrer 2015-04-13 20:38:29 +02:00
parent 37f228b049
commit 95e23dc8a9
69 changed files with 976 additions and 546 deletions

View file

@ -37,7 +37,7 @@ public class FiatAccount implements Serializable {
public static final long WEEK_IN_BLOCKS = DAY_IN_BLOCKS * 7; public static final long WEEK_IN_BLOCKS = DAY_IN_BLOCKS * 7;
public enum Type { public enum Type {
IRC("", "", 0), IRC("", "", 2),
SEPA("IBAN", "BIC", WEEK_IN_BLOCKS), SEPA("IBAN", "BIC", WEEK_IN_BLOCKS),
WIRE("primary ID", "secondary ID", WEEK_IN_BLOCKS), WIRE("primary ID", "secondary ID", WEEK_IN_BLOCKS),
INTERNATIONAL("primary ID", "secondary ID", 2 * WEEK_IN_BLOCKS), INTERNATIONAL("primary ID", "secondary ID", 2 * WEEK_IN_BLOCKS),

View file

@ -252,11 +252,13 @@ public class BootstrappedPeerBuilder {
} }
private void bootstrap() { private void bootstrap() {
log.trace("start bootstrap");
FutureBootstrap futureBootstrap = peer.bootstrap().peerAddress(getBootstrapAddress()).start(); FutureBootstrap futureBootstrap = peer.bootstrap().peerAddress(getBootstrapAddress()).start();
futureBootstrap.addListener(new BaseFutureListener<BaseFuture>() { futureBootstrap.addListener(new BaseFutureListener<BaseFuture>() {
@Override @Override
public void operationComplete(BaseFuture future) throws Exception { public void operationComplete(BaseFuture future) throws Exception {
if (futureBootstrap.isSuccess()) { if (futureBootstrap.isSuccess()) {
log.trace("bootstrap complete");
settableFuture.set(peerDHT); settableFuture.set(peerDHT);
} }
else { else {

View file

@ -22,9 +22,9 @@ import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.crypto.KeyRing; import io.bitsquare.crypto.KeyRing;
import io.bitsquare.crypto.PubKeyRing; import io.bitsquare.crypto.PubKeyRing;
import io.bitsquare.crypto.SealedAndSignedMessage; import io.bitsquare.crypto.SealedAndSignedMessage;
import io.bitsquare.offer.OfferBookService;
import io.bitsquare.p2p.MailboxMessagesResultHandler; import io.bitsquare.p2p.MailboxMessagesResultHandler;
import io.bitsquare.p2p.MailboxService; import io.bitsquare.p2p.MailboxService;
import io.bitsquare.trade.offer.OfferBookService;
import java.io.IOException; import java.io.IOException;

View file

@ -17,8 +17,8 @@
package io.bitsquare.trade; package io.bitsquare.trade;
import io.bitsquare.offer.Offer;
import io.bitsquare.storage.Storage; import io.bitsquare.storage.Storage;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.protocol.trade.BuyerAsOffererProtocol; import io.bitsquare.trade.protocol.trade.BuyerAsOffererProtocol;
import io.bitsquare.trade.protocol.trade.BuyerProtocol; import io.bitsquare.trade.protocol.trade.BuyerProtocol;
@ -42,7 +42,7 @@ public class BuyerAsOffererTrade extends OffererTrade implements BuyerTrade, Ser
// Constructor, initialization // Constructor, initialization
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public BuyerAsOffererTrade(Offer offer, Storage<? extends TradeList> storage) { public BuyerAsOffererTrade(Offer offer, Storage<? extends TradableList> storage) {
super(offer, storage); super(offer, storage);
log.trace("Created by constructor"); log.trace("Created by constructor");
} }

View file

@ -17,9 +17,9 @@
package io.bitsquare.trade; package io.bitsquare.trade;
import io.bitsquare.offer.Offer;
import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.Peer;
import io.bitsquare.storage.Storage; import io.bitsquare.storage.Storage;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.protocol.trade.BuyerAsTakerProtocol; import io.bitsquare.trade.protocol.trade.BuyerAsTakerProtocol;
import io.bitsquare.trade.protocol.trade.BuyerProtocol; import io.bitsquare.trade.protocol.trade.BuyerProtocol;
@ -43,7 +43,7 @@ public class BuyerAsTakerTrade extends TakerTrade implements BuyerTrade, Seriali
// Constructor, initialization // Constructor, initialization
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public BuyerAsTakerTrade(Offer offer, Coin tradeAmount, Peer tradingPeer, Storage<? extends TradeList> storage) { public BuyerAsTakerTrade(Offer offer, Coin tradeAmount, Peer tradingPeer, Storage<? extends TradableList> storage) {
super(offer, tradeAmount, tradingPeer, storage); super(offer, tradeAmount, tradingPeer, storage);
log.trace("Created by constructor"); log.trace("Created by constructor");
} }

View file

@ -19,7 +19,7 @@ package io.bitsquare.trade;
import io.bitsquare.crypto.PubKeyRing; import io.bitsquare.crypto.PubKeyRing;
import io.bitsquare.fiat.FiatAccount; import io.bitsquare.fiat.FiatAccount;
import io.bitsquare.offer.Offer; import io.bitsquare.trade.offer.Offer;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;

View file

@ -17,8 +17,11 @@
package io.bitsquare.trade; package io.bitsquare.trade;
import io.bitsquare.offer.Offer; import io.bitsquare.p2p.Peer;
import io.bitsquare.storage.Storage; import io.bitsquare.storage.Storage;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.protocol.trade.OffererProtocol;
import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
import io.bitsquare.trade.states.OffererTradeState; import io.bitsquare.trade.states.OffererTradeState;
import io.bitsquare.trade.states.TradeState; import io.bitsquare.trade.states.TradeState;
@ -33,7 +36,7 @@ public abstract class OffererTrade extends Trade implements Serializable {
transient private static final Logger log = LoggerFactory.getLogger(BuyerAsOffererTrade.class); transient private static final Logger log = LoggerFactory.getLogger(BuyerAsOffererTrade.class);
public OffererTrade(Offer offer, Storage<? extends TradeList> storage) { public OffererTrade(Offer offer, Storage<? extends TradableList> storage) {
super(offer, storage); super(offer, storage);
log.trace("Created by constructor"); log.trace("Created by constructor");
} }
@ -41,10 +44,14 @@ public abstract class OffererTrade extends Trade implements Serializable {
@Override @Override
protected void initStates() { protected void initStates() {
processState = OffererTradeState.ProcessState.UNDEFINED; processState = OffererTradeState.ProcessState.UNDEFINED;
lifeCycleState = OffererTradeState.LifeCycleState.OFFER_OPEN; lifeCycleState = Trade.LifeCycleState.PREPARATION;
initStateProperties(); initStateProperties();
} }
public void handleTakeOfferRequest(TradeMessage message, Peer taker) {
((OffererProtocol) tradeProtocol).handleTakeOfferRequest(message, taker);
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Setter for Mutable objects // Setter for Mutable objects
@ -57,16 +64,16 @@ public abstract class OffererTrade extends Trade implements Serializable {
switch ((OffererTradeState.ProcessState) processState) { switch ((OffererTradeState.ProcessState) processState) {
case EXCEPTION: case EXCEPTION:
disposeProtocol(); disposeProtocol();
setLifeCycleState(OffererTradeState.LifeCycleState.FAILED); setLifeCycleState(Trade.LifeCycleState.FAILED);
break; break;
} }
} }
@Override @Override
public void setLifeCycleState(TradeState.LifeCycleState lifeCycleState) { public void setLifeCycleState(Trade.LifeCycleState lifeCycleState) {
super.setLifeCycleState(lifeCycleState); super.setLifeCycleState(lifeCycleState);
switch ((OffererTradeState.LifeCycleState) lifeCycleState) { switch (lifeCycleState) {
case FAILED: case FAILED:
disposeProtocol(); disposeProtocol();
break; break;

View file

@ -17,8 +17,8 @@
package io.bitsquare.trade; package io.bitsquare.trade;
import io.bitsquare.offer.Offer;
import io.bitsquare.storage.Storage; import io.bitsquare.storage.Storage;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.protocol.trade.SellerAsOffererProtocol; import io.bitsquare.trade.protocol.trade.SellerAsOffererProtocol;
import io.bitsquare.trade.protocol.trade.SellerProtocol; import io.bitsquare.trade.protocol.trade.SellerProtocol;
@ -40,7 +40,7 @@ public class SellerAsOffererTrade extends OffererTrade implements SellerTrade, S
// Constructor, initialization // Constructor, initialization
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public SellerAsOffererTrade(Offer offer, Storage<? extends TradeList> storage) { public SellerAsOffererTrade(Offer offer, Storage<? extends TradableList> storage) {
super(offer, storage); super(offer, storage);
log.trace("Created by constructor"); log.trace("Created by constructor");
} }

View file

@ -17,9 +17,9 @@
package io.bitsquare.trade; package io.bitsquare.trade;
import io.bitsquare.offer.Offer;
import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.Peer;
import io.bitsquare.storage.Storage; import io.bitsquare.storage.Storage;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.protocol.trade.SellerAsTakerProtocol; import io.bitsquare.trade.protocol.trade.SellerAsTakerProtocol;
import io.bitsquare.trade.protocol.trade.SellerProtocol; import io.bitsquare.trade.protocol.trade.SellerProtocol;
import io.bitsquare.trade.states.TakerTradeState; import io.bitsquare.trade.states.TakerTradeState;
@ -43,7 +43,7 @@ public class SellerAsTakerTrade extends TakerTrade implements SellerTrade, Seria
// Constructor, initialization // Constructor, initialization
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public SellerAsTakerTrade(Offer offer, Coin tradeAmount, Peer tradingPeer, Storage<? extends TradeList> storage) { public SellerAsTakerTrade(Offer offer, Coin tradeAmount, Peer tradingPeer, Storage<? extends TradableList> storage) {
super(offer, tradeAmount, tradingPeer, storage); super(offer, tradeAmount, tradingPeer, storage);
log.trace("Created by constructor"); log.trace("Created by constructor");
} }

View file

@ -17,9 +17,9 @@
package io.bitsquare.trade; package io.bitsquare.trade;
import io.bitsquare.offer.Offer;
import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.Peer;
import io.bitsquare.storage.Storage; import io.bitsquare.storage.Storage;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.protocol.trade.TakerProtocol; import io.bitsquare.trade.protocol.trade.TakerProtocol;
import io.bitsquare.trade.states.TakerTradeState; import io.bitsquare.trade.states.TakerTradeState;
import io.bitsquare.trade.states.TradeState; import io.bitsquare.trade.states.TradeState;
@ -37,7 +37,7 @@ public abstract class TakerTrade extends Trade implements Serializable {
transient private static final Logger log = LoggerFactory.getLogger(BuyerAsTakerTrade.class); transient private static final Logger log = LoggerFactory.getLogger(BuyerAsTakerTrade.class);
public TakerTrade(Offer offer, Coin tradeAmount, Peer tradingPeer, Storage<? extends TradeList> storage) { public TakerTrade(Offer offer, Coin tradeAmount, Peer tradingPeer, Storage<? extends TradableList> storage) {
super(offer, tradeAmount, tradingPeer, storage); super(offer, tradeAmount, tradingPeer, storage);
log.trace("Created by constructor"); log.trace("Created by constructor");
} }
@ -45,7 +45,7 @@ public abstract class TakerTrade extends Trade implements Serializable {
@Override @Override
protected void initStates() { protected void initStates() {
processState = TakerTradeState.ProcessState.UNDEFINED; processState = TakerTradeState.ProcessState.UNDEFINED;
lifeCycleState = TakerTradeState.LifeCycleState.PENDING; lifeCycleState = Trade.LifeCycleState.PENDING;
initStateProperties(); initStateProperties();
} }
@ -66,16 +66,16 @@ public abstract class TakerTrade extends Trade implements Serializable {
switch ((TakerTradeState.ProcessState) processState) { switch ((TakerTradeState.ProcessState) processState) {
case EXCEPTION: case EXCEPTION:
disposeProtocol(); disposeProtocol();
setLifeCycleState(TakerTradeState.LifeCycleState.FAILED); setLifeCycleState(Trade.LifeCycleState.FAILED);
break; break;
} }
} }
@Override @Override
public void setLifeCycleState(TradeState.LifeCycleState lifeCycleState) { public void setLifeCycleState(Trade.LifeCycleState lifeCycleState) {
super.setLifeCycleState(lifeCycleState); super.setLifeCycleState(lifeCycleState);
switch ((TakerTradeState.LifeCycleState) lifeCycleState) { switch (lifeCycleState) {
case FAILED: case FAILED:
disposeProtocol(); disposeProtocol();
break; break;

View file

@ -0,0 +1,32 @@
/*
* 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 io.bitsquare.trade.offer.Offer;
import java.io.Serializable;
import java.util.Date;
public interface Tradable extends Serializable {
Offer getOffer();
Date getDate();
String getId();
}

View file

@ -31,13 +31,13 @@ import javafx.collections.ObservableList;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public class TradeList<T> extends ArrayList<T> implements Serializable { public class TradableList<T extends Tradable> extends ArrayList<T> implements Serializable {
// That object is saved to disc. We need to take care of changes to not break deserialization. // That object is saved to disc. We need to take care of changes to not break deserialization.
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
transient private static final Logger log = LoggerFactory.getLogger(TradeList.class); transient private static final Logger log = LoggerFactory.getLogger(TradableList.class);
transient final private Storage<TradeList> storage; transient final private Storage<TradableList<T>> storage;
// Use getObservableList() also class locally, to be sure that object exists in case we use the object as deserialized form // Use getObservableList() also class locally, to be sure that object exists in case we use the object as deserialized form
transient private ObservableList<T> observableList; transient private ObservableList<T> observableList;
@ -47,11 +47,12 @@ public class TradeList<T> extends ArrayList<T> implements Serializable {
// Constructor // Constructor
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public TradeList(Storage<TradeList> storage, String fileName) { public TradableList(Storage<TradableList<T>> storage, String fileName) {
log.trace("Created by constructor"); log.trace("Created by constructor");
this.storage = storage; this.storage = storage;
TradeList persisted = storage.initAndGetPersisted(this, fileName); TradableList persisted = storage.initAndGetPersisted(this, fileName);
if (persisted != null) { if (persisted != null) {
this.addAll(persisted); this.addAll(persisted);
} }
@ -64,17 +65,17 @@ public class TradeList<T> extends ArrayList<T> implements Serializable {
} }
@Override @Override
public boolean add(T trade) { public boolean add(T tradable) {
boolean result = super.add(trade); boolean result = super.add(tradable);
getObservableList().add(trade); getObservableList().add(tradable);
storage.queueUpForSave(); storage.queueUpForSave();
return result; return result;
} }
@Override @Override
public boolean remove(Object trade) { public boolean remove(Object tradable) {
boolean result = super.remove(trade); boolean result = super.remove(tradable);
getObservableList().remove(trade); getObservableList().remove(tradable);
storage.queueUpForSave(); storage.queueUpForSave();
return result; return result;
} }

View file

@ -25,11 +25,11 @@ import io.bitsquare.common.taskrunner.Model;
import io.bitsquare.crypto.CryptoService; import io.bitsquare.crypto.CryptoService;
import io.bitsquare.crypto.KeyRing; import io.bitsquare.crypto.KeyRing;
import io.bitsquare.crypto.MessageWithPubKey; import io.bitsquare.crypto.MessageWithPubKey;
import io.bitsquare.offer.Offer;
import io.bitsquare.p2p.AddressService; import io.bitsquare.p2p.AddressService;
import io.bitsquare.p2p.MessageService; import io.bitsquare.p2p.MessageService;
import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.Peer;
import io.bitsquare.storage.Storage; import io.bitsquare.storage.Storage;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.protocol.trade.ProcessModel; import io.bitsquare.trade.protocol.trade.ProcessModel;
import io.bitsquare.trade.protocol.trade.TradeProtocol; import io.bitsquare.trade.protocol.trade.TradeProtocol;
import io.bitsquare.trade.states.TradeState; import io.bitsquare.trade.states.TradeState;
@ -66,12 +66,19 @@ import org.slf4j.LoggerFactory;
* Holds all data which are relevant to the trade, but not those which are only needed in the trade process as shared data between tasks. Those data are * Holds all data which are relevant to the trade, but not those which are only needed in the trade process as shared data between tasks. Those data are
* stored in the task model. * stored in the task model.
*/ */
abstract public class Trade implements Model, Serializable { abstract public class Trade implements Tradable, Model, Serializable {
// That object is saved to disc. We need to take care of changes to not break deserialization. // That object is saved to disc. We need to take care of changes to not break deserialization.
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private transient static final Logger log = LoggerFactory.getLogger(Trade.class); private transient static final Logger log = LoggerFactory.getLogger(Trade.class);
public enum LifeCycleState {
PREPARATION,
PENDING,
COMPLETED,
FAILED
}
// Mutable // Mutable
private Coin tradeAmount; private Coin tradeAmount;
private Peer tradingPeer; private Peer tradingPeer;
@ -85,9 +92,9 @@ abstract public class Trade implements Model, Serializable {
// Transient/Immutable // Transient/Immutable
private transient ObjectProperty<TradeState.ProcessState> processStateProperty; private transient ObjectProperty<TradeState.ProcessState> processStateProperty;
private transient ObjectProperty<TradeState.LifeCycleState> lifeCycleStateProperty; private transient ObjectProperty<Trade.LifeCycleState> lifeCycleStateProperty;
// Trades are saved in the TradeList // Trades are saved in the TradeList
transient private Storage<? extends TradeList> storage; transient private Storage<? extends TradableList> storage;
transient protected TradeProtocol tradeProtocol; transient protected TradeProtocol tradeProtocol;
// Immutable // Immutable
@ -99,7 +106,7 @@ abstract public class Trade implements Model, Serializable {
private MessageWithPubKey messageWithPubKey; private MessageWithPubKey messageWithPubKey;
private Date takeOfferDate; private Date takeOfferDate;
protected TradeState.ProcessState processState; protected TradeState.ProcessState processState;
protected TradeState.LifeCycleState lifeCycleState; protected Trade.LifeCycleState lifeCycleState;
private Transaction depositTx; private Transaction depositTx;
private Contract contract; private Contract contract;
private String contractAsJson; private String contractAsJson;
@ -117,7 +124,7 @@ abstract public class Trade implements Model, Serializable {
// Constructor, initialization // Constructor, initialization
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
protected Trade(Offer offer, Storage<? extends TradeList> storage) { protected Trade(Offer offer, Storage<? extends TradableList> storage) {
log.trace("Created by constructor"); log.trace("Created by constructor");
this.offer = offer; this.offer = offer;
this.storage = storage; this.storage = storage;
@ -135,7 +142,7 @@ abstract public class Trade implements Model, Serializable {
// taker // taker
protected Trade(Offer offer, Coin tradeAmount, Peer tradingPeer, protected Trade(Offer offer, Coin tradeAmount, Peer tradingPeer,
Storage<? extends TradeList> storage) { Storage<? extends TradableList> storage) {
this(offer, storage); this(offer, storage);
this.tradeAmount = tradeAmount; this.tradeAmount = tradeAmount;
@ -227,7 +234,7 @@ abstract public class Trade implements Model, Serializable {
this.messageWithPubKey = messageWithPubKey; this.messageWithPubKey = messageWithPubKey;
} }
public void setStorage(Storage<? extends TradeList> storage) { public void setStorage(Storage<? extends TradableList> storage) {
this.storage = storage; this.storage = storage;
} }
@ -237,7 +244,7 @@ abstract public class Trade implements Model, Serializable {
storage.queueUpForSave(); storage.queueUpForSave();
} }
public void setLifeCycleState(TradeState.LifeCycleState lifeCycleState) { public void setLifeCycleState(Trade.LifeCycleState lifeCycleState) {
this.lifeCycleState = lifeCycleState; this.lifeCycleState = lifeCycleState;
lifeCycleStateProperty.set(lifeCycleState); lifeCycleStateProperty.set(lifeCycleState);
storage.queueUpForSave(); storage.queueUpForSave();
@ -302,7 +309,7 @@ abstract public class Trade implements Model, Serializable {
return processStateProperty; return processStateProperty;
} }
public ReadOnlyObjectProperty<? extends TradeState.LifeCycleState> lifeCycleStateProperty() { public ReadOnlyObjectProperty<Trade.LifeCycleState> lifeCycleStateProperty() {
return lifeCycleStateProperty; return lifeCycleStateProperty;
} }

View file

@ -22,39 +22,37 @@ import io.bitsquare.btc.AddressEntry;
import io.bitsquare.btc.BlockChainService; import io.bitsquare.btc.BlockChainService;
import io.bitsquare.btc.TradeWalletService; import io.bitsquare.btc.TradeWalletService;
import io.bitsquare.btc.WalletService; import io.bitsquare.btc.WalletService;
import io.bitsquare.common.handlers.ErrorMessageHandler;
import io.bitsquare.common.handlers.FaultHandler; import io.bitsquare.common.handlers.FaultHandler;
import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.crypto.CryptoService; import io.bitsquare.crypto.CryptoService;
import io.bitsquare.crypto.KeyRing; import io.bitsquare.crypto.KeyRing;
import io.bitsquare.crypto.MessageWithPubKey; import io.bitsquare.crypto.MessageWithPubKey;
import io.bitsquare.crypto.SealedAndSignedMessage; import io.bitsquare.crypto.SealedAndSignedMessage;
import io.bitsquare.fiat.FiatAccount;
import io.bitsquare.offer.Offer;
import io.bitsquare.offer.OfferBookService;
import io.bitsquare.p2p.AddressService; import io.bitsquare.p2p.AddressService;
import io.bitsquare.p2p.DecryptedMessageHandler;
import io.bitsquare.p2p.MailboxMessage; import io.bitsquare.p2p.MailboxMessage;
import io.bitsquare.p2p.MailboxService; import io.bitsquare.p2p.MailboxService;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.MessageService; import io.bitsquare.p2p.MessageService;
import io.bitsquare.p2p.Peer;
import io.bitsquare.storage.Storage; import io.bitsquare.storage.Storage;
import io.bitsquare.trade.closed.ClosedTradableManager;
import io.bitsquare.trade.handlers.TakeOfferResultHandler; import io.bitsquare.trade.handlers.TakeOfferResultHandler;
import io.bitsquare.trade.handlers.TransactionResultHandler; import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.protocol.availability.CheckOfferAvailabilityModel; import io.bitsquare.trade.offer.OpenOffer;
import io.bitsquare.trade.protocol.availability.CheckOfferAvailabilityProtocol; import io.bitsquare.trade.offer.OpenOfferManager;
import io.bitsquare.trade.protocol.placeoffer.PlaceOfferModel; import io.bitsquare.trade.protocol.availability.OfferAvailabilityModel;
import io.bitsquare.trade.protocol.placeoffer.PlaceOfferProtocol; import io.bitsquare.trade.protocol.availability.OfferAvailabilityProtocol;
import io.bitsquare.trade.protocol.trade.messages.RequestDepositTxInputsMessage;
import io.bitsquare.trade.protocol.trade.messages.RequestPayDepositMessage;
import io.bitsquare.trade.protocol.trade.messages.TradeMessage; import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
import io.bitsquare.trade.states.OffererTradeState; import io.bitsquare.trade.states.OffererTradeState;
import io.bitsquare.trade.states.TakerTradeState;
import io.bitsquare.user.AccountSettings;
import io.bitsquare.user.User; import io.bitsquare.user.User;
import org.bitcoinj.core.AddressFormatException; import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.bitcoinj.core.InsufficientMoneyException; import org.bitcoinj.core.InsufficientMoneyException;
import org.bitcoinj.core.Transaction; import org.bitcoinj.core.Transaction;
import org.bitcoinj.utils.Fiat;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
@ -77,12 +75,13 @@ import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import static io.bitsquare.util.Validator.nonEmptyStringOf;
public class TradeManager { public class TradeManager {
private static final Logger log = LoggerFactory.getLogger(TradeManager.class); private static final Logger log = LoggerFactory.getLogger(TradeManager.class);
private final User user; private final User user;
private KeyRing keyRing; private final KeyRing keyRing;
private final AccountSettings accountSettings;
private final MessageService messageService; private final MessageService messageService;
private final MailboxService mailboxService; private final MailboxService mailboxService;
private final AddressService addressService; private final AddressService addressService;
@ -90,17 +89,13 @@ public class TradeManager {
private final WalletService walletService; private final WalletService walletService;
private final TradeWalletService tradeWalletService; private final TradeWalletService tradeWalletService;
private final CryptoService<MailboxMessage> cryptoService; private final CryptoService<MailboxMessage> cryptoService;
private final OfferBookService offerBookService; private OpenOfferManager openOfferManager;
private ClosedTradableManager closedTradableManager;
private final ArbitrationRepository arbitrationRepository; private final ArbitrationRepository arbitrationRepository;
private final Map<String, CheckOfferAvailabilityProtocol> checkOfferAvailabilityProtocolMap = new HashMap<>(); private final Map<String, OfferAvailabilityProtocol> checkOfferAvailabilityProtocolMap = new HashMap<>();
private final Storage<TradeList> pendingTradesStorage; private final Storage<TradableList<Trade>> pendingTradesStorage;
private final Storage<TradeList> openOfferTradesStorage; private final TradableList<Trade> pendingTrades;
private final TradeList<Trade> openOfferTrades;
private final TradeList<Trade> pendingTrades;
private final TradeList<Trade> closedTrades;
private boolean shutDownRequested;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -110,7 +105,6 @@ public class TradeManager {
@Inject @Inject
public TradeManager(User user, public TradeManager(User user,
KeyRing keyRing, KeyRing keyRing,
AccountSettings accountSettings,
MessageService messageService, MessageService messageService,
MailboxService mailboxService, MailboxService mailboxService,
AddressService addressService, AddressService addressService,
@ -118,12 +112,12 @@ public class TradeManager {
WalletService walletService, WalletService walletService,
TradeWalletService tradeWalletService, TradeWalletService tradeWalletService,
CryptoService<MailboxMessage> cryptoService, CryptoService<MailboxMessage> cryptoService,
OfferBookService offerBookService, OpenOfferManager openOfferManager,
ClosedTradableManager closedTradableManager,
ArbitrationRepository arbitrationRepository, ArbitrationRepository arbitrationRepository,
@Named("storage.dir") File storageDir) { @Named("storage.dir") File storageDir) {
this.user = user; this.user = user;
this.keyRing = keyRing; this.keyRing = keyRing;
this.accountSettings = accountSettings;
this.messageService = messageService; this.messageService = messageService;
this.mailboxService = mailboxService; this.mailboxService = mailboxService;
this.addressService = addressService; this.addressService = addressService;
@ -131,20 +125,12 @@ public class TradeManager {
this.walletService = walletService; this.walletService = walletService;
this.tradeWalletService = tradeWalletService; this.tradeWalletService = tradeWalletService;
this.cryptoService = cryptoService; this.cryptoService = cryptoService;
this.offerBookService = offerBookService; this.openOfferManager = openOfferManager;
this.closedTradableManager = closedTradableManager;
this.arbitrationRepository = arbitrationRepository; this.arbitrationRepository = arbitrationRepository;
openOfferTradesStorage = new Storage<>(storageDir);
pendingTradesStorage = new Storage<>(storageDir); pendingTradesStorage = new Storage<>(storageDir);
this.pendingTrades = new TradableList<>(pendingTradesStorage, "PendingTrades");
this.openOfferTrades = new TradeList<>(openOfferTradesStorage, "OpenOfferTrades");
this.pendingTrades = new TradeList<>(pendingTradesStorage, "PendingTrades");
this.closedTrades = new TradeList<>(new Storage<>(storageDir), "ClosedTrades");
// In case the app did get killed the shutDown from the modules is not called, so we use a shutdown hook
Thread shutDownHookThread = new Thread(TradeManager.this::shutDown, "TradeManager.ShutDownHook");
Runtime.getRuntime().addShutdownHook(shutDownHookThread);
} }
@ -156,16 +142,6 @@ public class TradeManager {
// OffererAsBuyerProtocol listens for take offer requests, so we need to instantiate it early. // OffererAsBuyerProtocol listens for take offer requests, so we need to instantiate it early.
public void onAllServicesInitialized() { public void onAllServicesInitialized() {
log.trace("onAllServicesInitialized"); log.trace("onAllServicesInitialized");
for (Trade trade : openOfferTrades) {
Offer offer = trade.getOffer();
// We add own offers to offerbook when we go online again
offerBookService.addOffer(offer,
() -> log.debug("Successful removed open offer from DHT"),
(message, throwable) -> log.error("Remove open offer from DHT failed. " + message));
setupDepositPublishedListener(trade);
trade.setStorage(openOfferTradesStorage);
initTrade(trade);
}
// If there are messages in our mailbox we apply it and remove them from the DHT // If there are messages in our mailbox we apply it and remove them from the DHT
// We run that before initializing the pending trades to be sure the state is correct // We run that before initializing the pending trades to be sure the state is correct
@ -173,10 +149,69 @@ public class TradeManager {
(encryptedMailboxMessages) -> { (encryptedMailboxMessages) -> {
log.trace("mailboxService.getAllMessages success"); log.trace("mailboxService.getAllMessages success");
setMailboxMessagesToTrades(encryptedMailboxMessages); setMailboxMessagesToTrades(encryptedMailboxMessages);
//TODO testing emptyMailbox();
//emptyMailbox();
initPendingTrades(); initPendingTrades();
}); });
// Handler for incoming initial messages from taker
messageService.addDecryptedMessageHandler(new DecryptedMessageHandler() {
@Override
public void handleMessage(MessageWithPubKey messageWithPubKey, Peer sender) {
// We get an encrypted message but don't do the signature check as we don't know the peer yet.
// A basic sig check is in done also at decryption time
Message message = messageWithPubKey.getMessage();
// Those 2 messages are initial request form the taker.
// RequestPayDepositMessage is used also in case of SellerAsTaker but there it is handled in the protocol as it is not an initial request
if (message instanceof RequestDepositTxInputsMessage ||
(message instanceof RequestPayDepositMessage && ((RequestPayDepositMessage) message).isInitialRequest))
handleInitialTakeOfferRequest((TradeMessage) message, sender);
}
});
}
private void handleInitialTakeOfferRequest(TradeMessage message, Peer sender) {
log.trace("handleNewMessage: message = " + message.getClass().getSimpleName() + " from " + sender);
try {
nonEmptyStringOf(message.tradeId);
} catch (Throwable t) {
log.warn("Invalid requestDepositTxInputsMessage " + message.toString());
return;
}
Optional<OpenOffer> openOfferOptional = openOfferManager.findOpenOffer(message.tradeId);
if (openOfferOptional.isPresent() && openOfferOptional.get().getState() == OpenOffer.State.AVAILABLE) {
Offer offer = openOfferOptional.get().getOffer();
openOfferManager.reserveOpenOffer(openOfferOptional.get());
OffererTrade trade;
if (offer.getDirection() == Offer.Direction.BUY)
trade = new BuyerAsOffererTrade(offer, pendingTradesStorage);
else
trade = new SellerAsOffererTrade(offer, pendingTradesStorage);
trade.setStorage(pendingTradesStorage);
pendingTrades.add(trade);
initTrade(trade);
trade.handleTakeOfferRequest(message, sender);
setupDepositPublishedListener(trade);
}
else {
// TODO respond
//(RequestDepositTxInputsMessage)message.
// messageService.sendEncryptedMessage(sender,messageWithPubKey.getMessage().);
log.info("We received a take offer request but don't have that offer anymore.");
}
}
// Only after published we consider the openOffer as closed and the trade as completely initialized
private void setupDepositPublishedListener(Trade trade) {
trade.processStateProperty().addListener((ov, oldValue, newValue) -> {
log.debug("setupDepositPublishedListener state = " + newValue);
if (newValue == OffererTradeState.ProcessState.DEPOSIT_PUBLISHED) {
openOfferManager.closeOpenOffer(trade.getOffer());
trade.setTakeOfferDate(new Date());
}
});
} }
private void setMailboxMessagesToTrades(List<SealedAndSignedMessage> encryptedMessages) { private void setMailboxMessagesToTrades(List<SealedAndSignedMessage> encryptedMessages) {
@ -208,20 +243,13 @@ public class TradeManager {
} }
private void initPendingTrades() { private void initPendingTrades() {
log.trace("initPendingTrades");
List<Trade> failedTrades = new ArrayList<>(); List<Trade> failedTrades = new ArrayList<>();
for (Trade trade : pendingTrades) { for (Trade trade : pendingTrades) {
// We continue an interrupted trade. // We continue an interrupted trade.
// TODO if the peer has changed its IP address, we need to make another findPeer request. At the moment we use the peer stored in trade to // TODO if the peer has changed its IP address, we need to make another findPeer request. At the moment we use the peer stored in trade to
// continue the trade, but that might fail. // continue the trade, but that might fail.
boolean failed = false; if (trade.lifeCycleState == Trade.LifeCycleState.FAILED) {
if (trade instanceof TakerTrade)
failed = trade.lifeCycleState == TakerTradeState.LifeCycleState.FAILED;
else if (trade instanceof OffererTrade)
failed = trade.lifeCycleState == OffererTradeState.LifeCycleState.FAILED;
if (failed) {
failedTrades.add(trade); failedTrades.add(trade);
} }
else { else {
@ -232,63 +260,21 @@ public class TradeManager {
} }
for (Trade trade : failedTrades) { for (Trade trade : failedTrades) {
pendingTrades.remove(trade); pendingTrades.remove(trade);
closedTrades.add(trade); closedTradableManager.add(trade);
}
}
public void shutDown() {
if (!shutDownRequested) {
log.debug("shutDown");
shutDownRequested = true;
// we remove own offers form offerbook when we go offline
for (Trade trade : openOfferTrades) {
Offer offer = trade.getOffer();
offerBookService.removeOfferAtShutDown(offer);
}
} }
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Offer // Called from Offerbook when offer gets removed from DHT
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void placeOffer(String id, public void onOfferRemovedFromRemoteOfferBook(Offer offer) {
Offer.Direction direction, disposeCheckOfferAvailabilityRequest(offer);
Fiat price,
Coin amount,
Coin minAmount,
TransactionResultHandler resultHandler,
ErrorMessageHandler errorMessageHandler) {
FiatAccount fiatAccount = user.currentFiatAccountProperty().get();
Offer offer = new Offer(id,
keyRing.getPubKeyRing(),
direction,
price.getValue(),
amount,
minAmount,
fiatAccount.type,
fiatAccount.currencyCode,
fiatAccount.country,
fiatAccount.id,
accountSettings.getAcceptedArbitratorIds(),
accountSettings.getSecurityDeposit(),
accountSettings.getAcceptedCountries(),
accountSettings.getAcceptedLanguageLocaleCodes());
PlaceOfferModel model = new PlaceOfferModel(offer, walletService, tradeWalletService, offerBookService);
PlaceOfferProtocol placeOfferProtocol = new PlaceOfferProtocol(
model,
transaction -> handlePlaceOfferResult(transaction, offer, resultHandler),
errorMessageHandler::handleErrorMessage
);
placeOfferProtocol.placeOffer();
} }
private void handlePlaceOfferResult(Transaction transaction, Offer offer, TransactionResultHandler resultHandler) {
/* private void handlePlaceOfferResult(Transaction transaction, Offer offer, TransactionResultHandler resultHandler) {
Trade trade; Trade trade;
if (offer.getDirection() == Offer.Direction.BUY) if (offer.getDirection() == Offer.Direction.BUY)
trade = new BuyerAsOffererTrade(offer, openOfferTradesStorage); trade = new BuyerAsOffererTrade(offer, openOfferTradesStorage);
@ -299,9 +285,9 @@ public class TradeManager {
initTrade(trade); initTrade(trade);
setupDepositPublishedListener(trade); setupDepositPublishedListener(trade);
resultHandler.handleResult(transaction); resultHandler.handleResult(transaction);
} }*/
private void setupDepositPublishedListener(Trade trade) { /* private void setupDepositPublishedListener(Trade trade) {
trade.processStateProperty().addListener((ov, oldValue, newValue) -> { trade.processStateProperty().addListener((ov, oldValue, newValue) -> {
log.debug("setupDepositPublishedListener state = " + newValue); log.debug("setupDepositPublishedListener state = " + newValue);
if (newValue == OffererTradeState.ProcessState.DEPOSIT_PUBLISHED) { if (newValue == OffererTradeState.ProcessState.DEPOSIT_PUBLISHED) {
@ -314,13 +300,13 @@ public class TradeManager {
trade.setStorage(pendingTradesStorage); trade.setStorage(pendingTradesStorage);
} }
}); });
} }*/
public void onCancelOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { /* public void onCancelOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
removeOpenOffer(offer, resultHandler, errorMessageHandler, true); removeOpenOffer(offer, resultHandler, errorMessageHandler, true);
} }
*/
private void removeOpenOffer(Offer offer, /* private void removeOpenOffer(Offer offer,
ResultHandler resultHandler, ResultHandler resultHandler,
ErrorMessageHandler errorMessageHandler, ErrorMessageHandler errorMessageHandler,
boolean isCancelRequest) { boolean isCancelRequest) {
@ -345,7 +331,7 @@ public class TradeManager {
resultHandler.handleResult(); resultHandler.handleResult();
}, },
(message, throwable) -> errorMessageHandler.handleErrorMessage(message)); (message, throwable) -> errorMessageHandler.handleErrorMessage(message));
} }*/
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -354,13 +340,13 @@ public class TradeManager {
public void checkOfferAvailability(Offer offer) { public void checkOfferAvailability(Offer offer) {
if (!checkOfferAvailabilityProtocolMap.containsKey(offer.getId())) { if (!checkOfferAvailabilityProtocolMap.containsKey(offer.getId())) {
CheckOfferAvailabilityModel model = new CheckOfferAvailabilityModel( OfferAvailabilityModel model = new OfferAvailabilityModel(
offer, offer,
keyRing.getPubKeyRing(), keyRing.getPubKeyRing(),
messageService, messageService,
addressService); addressService);
CheckOfferAvailabilityProtocol protocol = new CheckOfferAvailabilityProtocol(model, OfferAvailabilityProtocol protocol = new OfferAvailabilityProtocol(model,
() -> disposeCheckOfferAvailabilityRequest(offer), () -> disposeCheckOfferAvailabilityRequest(offer),
(errorMessage) -> disposeCheckOfferAvailabilityRequest(offer)); (errorMessage) -> disposeCheckOfferAvailabilityRequest(offer));
checkOfferAvailabilityProtocolMap.put(offer.getId(), protocol); checkOfferAvailabilityProtocolMap.put(offer.getId(), protocol);
@ -378,15 +364,15 @@ public class TradeManager {
// First we check if offer is still available then we create the trade with the protocol // First we check if offer is still available then we create the trade with the protocol
public void requestTakeOffer(Coin amount, Offer offer, TakeOfferResultHandler takeOfferResultHandler) { public void requestTakeOffer(Coin amount, Offer offer, TakeOfferResultHandler takeOfferResultHandler) {
CheckOfferAvailabilityModel model = new CheckOfferAvailabilityModel(offer, keyRing.getPubKeyRing(), messageService, addressService); OfferAvailabilityModel model = new OfferAvailabilityModel(offer, keyRing.getPubKeyRing(), messageService, addressService);
CheckOfferAvailabilityProtocol availabilityProtocol = new CheckOfferAvailabilityProtocol(model, OfferAvailabilityProtocol availabilityProtocol = new OfferAvailabilityProtocol(model,
() -> createTrade(amount, offer, model, takeOfferResultHandler), () -> createTrade(amount, offer, model, takeOfferResultHandler),
(errorMessage) -> disposeCheckOfferAvailabilityRequest(offer)); (errorMessage) -> disposeCheckOfferAvailabilityRequest(offer));
checkOfferAvailabilityProtocolMap.put(offer.getId(), availabilityProtocol); checkOfferAvailabilityProtocolMap.put(offer.getId(), availabilityProtocol);
availabilityProtocol.checkOfferAvailability(); availabilityProtocol.checkOfferAvailability();
} }
private void createTrade(Coin amount, Offer offer, CheckOfferAvailabilityModel model, TakeOfferResultHandler private void createTrade(Coin amount, Offer offer, OfferAvailabilityModel model, TakeOfferResultHandler
takeOfferResultHandler) { takeOfferResultHandler) {
disposeCheckOfferAvailabilityRequest(offer); disposeCheckOfferAvailabilityRequest(offer);
if (offer.getState() == Offer.State.AVAILABLE) { if (offer.getState() == Offer.State.AVAILABLE) {
@ -419,13 +405,10 @@ public class TradeManager {
public void onSuccess(@javax.annotation.Nullable Transaction transaction) { public void onSuccess(@javax.annotation.Nullable Transaction transaction) {
if (transaction != null) { if (transaction != null) {
log.info("onWithdraw onSuccess tx ID:" + transaction.getHashAsString()); log.info("onWithdraw onSuccess tx ID:" + transaction.getHashAsString());
if (trade instanceof OffererTrade) trade.setLifeCycleState(Trade.LifeCycleState.COMPLETED);
trade.setLifeCycleState(OffererTradeState.LifeCycleState.COMPLETED);
else if (trade instanceof TakerTrade)
trade.setLifeCycleState(TakerTradeState.LifeCycleState.COMPLETED);
pendingTrades.remove(trade); pendingTrades.remove(trade);
closedTrades.add(trade); closedTradableManager.add(trade);
resultHandler.handleResult(); resultHandler.handleResult();
} }
@ -452,27 +435,27 @@ public class TradeManager {
// Called from Offerbook when offer gets removed from DHT // Called from Offerbook when offer gets removed from DHT
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void onOfferRemovedFromRemoteOfferBook(Offer offer) { /* public void onOfferRemovedFromRemoteOfferBook(Offer offer) {
disposeCheckOfferAvailabilityRequest(offer); disposeCheckOfferAvailabilityRequest(offer);
} }
*/
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Getters // Getters
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public ObservableList<Trade> getOpenOfferTrades() { /* public ObservableList<Trade> getOpenOfferTrades() {
return openOfferTrades.getObservableList(); return openOfferTrades.getObservableList();
} }*/
public ObservableList<Trade> getPendingTrades() { public ObservableList<Trade> getPendingTrades() {
return pendingTrades.getObservableList(); return pendingTrades.getObservableList();
} }
public ObservableList<Trade> getClosedTrades() { /* public ObservableList<Trade> getClosedTrades() {
return closedTrades.getObservableList(); return closedTrades.getObservableList();
} }
*/
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Misc // Misc
@ -480,7 +463,7 @@ public class TradeManager {
private void disposeCheckOfferAvailabilityRequest(Offer offer) { private void disposeCheckOfferAvailabilityRequest(Offer offer) {
if (checkOfferAvailabilityProtocolMap.containsKey(offer.getId())) { if (checkOfferAvailabilityProtocolMap.containsKey(offer.getId())) {
CheckOfferAvailabilityProtocol protocol = checkOfferAvailabilityProtocolMap.get(offer.getId()); OfferAvailabilityProtocol protocol = checkOfferAvailabilityProtocolMap.get(offer.getId());
protocol.cancel(); protocol.cancel();
checkOfferAvailabilityProtocolMap.remove(offer.getId()); checkOfferAvailabilityProtocolMap.remove(offer.getId());
} }
@ -499,6 +482,6 @@ public class TradeManager {
} }
public boolean isMyOffer(Offer offer) { public boolean isMyOffer(Offer offer) {
return offer.getPubKeyRing().getHashString().equals(keyRing.getPubKeyRing().getHashString()); return offer.isMyOffer(keyRing);
} }
} }

View file

@ -18,8 +18,8 @@
package io.bitsquare.trade; package io.bitsquare.trade;
import io.bitsquare.BitsquareModule; import io.bitsquare.BitsquareModule;
import io.bitsquare.trade.closed.ClosedTradableManager;
import com.google.inject.Injector;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -37,11 +37,12 @@ public class TradeModule extends BitsquareModule {
@Override @Override
protected void configure() { protected void configure() {
bind(TradeManager.class).in(Singleton.class); bind(TradeManager.class).in(Singleton.class);
bind(ClosedTradableManager.class).in(Singleton.class);
} }
@Override /* @Override
protected void doClose(Injector injector) { protected void doClose(Injector injector) {
log.trace("doClose " + getClass().getSimpleName()); log.trace("doClose " + getClass().getSimpleName());
injector.getInstance(TradeManager.class).shutDown(); injector.getInstance(TradeManager.class).shutDown();
} }*/
} }

View file

@ -0,0 +1,62 @@
/*
* 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.closed;
import io.bitsquare.crypto.KeyRing;
import io.bitsquare.storage.Storage;
import io.bitsquare.trade.Tradable;
import io.bitsquare.trade.TradableList;
import io.bitsquare.trade.offer.Offer;
import com.google.inject.Inject;
import java.io.File;
import javax.inject.Named;
import javafx.collections.ObservableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ClosedTradableManager {
private static final Logger log = LoggerFactory.getLogger(ClosedTradableManager.class);
private final TradableList<Tradable> closedTrades;
private KeyRing keyRing;
@Inject
public ClosedTradableManager(KeyRing keyRing, @Named("storage.dir") File storageDir) {
this.keyRing = keyRing;
this.closedTrades = new TradableList<>(new Storage<>(storageDir), "ClosedTrades");
}
public void add(Tradable tradable) {
closedTrades.add(tradable);
}
public boolean wasMyOffer(Offer offer) {
return offer.isMyOffer(keyRing);
}
public ObservableList<Tradable> getClosedTrades() {
return closedTrades.getObservableList();
}
}

View file

@ -15,9 +15,10 @@
* 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.offer; package io.bitsquare.trade.offer;
import io.bitsquare.btc.Restrictions; import io.bitsquare.btc.Restrictions;
import io.bitsquare.crypto.KeyRing;
import io.bitsquare.crypto.PubKeyRing; import io.bitsquare.crypto.PubKeyRing;
import io.bitsquare.fiat.FiatAccount; import io.bitsquare.fiat.FiatAccount;
import io.bitsquare.locale.Country; import io.bitsquare.locale.Country;
@ -51,12 +52,11 @@ public class Offer implements Serializable {
public enum Direction {BUY, SELL} public enum Direction {BUY, SELL}
public enum State { public enum State {
UNKNOWN, UNDEFINED,
AVAILABLE, AVAILABLE,
RESERVED, NOT_AVAILABLE,
REMOVED, REMOVED,
OFFERER_OFFLINE, OFFERER_OFFLINE
FAULT
} }
@ -83,7 +83,7 @@ public class Offer implements Serializable {
// Mutable property. Has to be set before offer is save in DHT as it changes the objects hash! // Mutable property. Has to be set before offer is save in DHT as it changes the objects hash!
private String offerFeePaymentTxID; private String offerFeePaymentTxID;
private State state = State.UNKNOWN; private State state = State.UNDEFINED;
// Those state properties are transient and only used at runtime! // Those state properties are transient and only used at runtime!
// don't access directly as it might be null; use getStateProperty() which creates an object if not instantiated // don't access directly as it might be null; use getStateProperty() which creates an object if not instantiated
@ -125,7 +125,7 @@ public class Offer implements Serializable {
this.acceptedLanguageCodes = acceptedLanguageCodes; this.acceptedLanguageCodes = acceptedLanguageCodes;
creationDate = new Date(); creationDate = new Date();
setState(State.UNKNOWN); setState(State.UNDEFINED);
} }
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
@ -160,6 +160,10 @@ public class Offer implements Serializable {
// TODO check upper and lower bounds for fiat // TODO check upper and lower bounds for fiat
} }
public boolean isMyOffer(KeyRing keyRing) {
return getPubKeyRing().getHashString().equals(keyRing.getPubKeyRing().getHashString());
}
public Fiat getVolumeByAmount(Coin amount) { public Fiat getVolumeByAmount(Coin amount) {
if (fiatPrice != 0 && amount != null && !amount.isZero()) if (fiatPrice != 0 && amount != null && !amount.isZero())
return new ExchangeRate(Fiat.valueOf(currencyCode, fiatPrice)).coinToFiat(amount); return new ExchangeRate(Fiat.valueOf(currencyCode, fiatPrice)).coinToFiat(amount);

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.offer; package io.bitsquare.trade.offer;
import io.bitsquare.common.handlers.FaultHandler; import io.bitsquare.common.handlers.FaultHandler;
import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.common.handlers.ResultHandler;

View file

@ -15,13 +15,20 @@
* 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.offer; package io.bitsquare.trade.offer;
import io.bitsquare.BitsquareModule; import io.bitsquare.BitsquareModule;
import com.google.inject.Injector;
import com.google.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
public abstract class OfferModule extends BitsquareModule { public abstract class OfferModule extends BitsquareModule {
private static final Logger log = LoggerFactory.getLogger(OfferModule.class);
protected OfferModule(Environment env) { protected OfferModule(Environment env) {
super(env); super(env);
@ -29,9 +36,16 @@ public abstract class OfferModule extends BitsquareModule {
@Override @Override
protected final void configure() { protected final void configure() {
bind(OpenOfferManager.class).in(Singleton.class);
doConfigure(); doConfigure();
} }
protected void doConfigure() { protected void doConfigure() {
} }
@Override
protected void doClose(Injector injector) {
log.trace("doClose " + getClass().getSimpleName());
injector.getInstance(OpenOfferManager.class).shutDown();
}
} }

View file

@ -0,0 +1,124 @@
/*
* 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.offer;
import io.bitsquare.storage.Storage;
import io.bitsquare.trade.Tradable;
import io.bitsquare.trade.TradableList;
import org.bitcoinj.utils.Threading;
import java.io.Serializable;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OpenOffer implements Tradable, Serializable {
// That object is saved to disc. We need to take care of changes to not break deserialization.
private static final long serialVersionUID = 1L;
private static final Logger log = LoggerFactory.getLogger(OpenOffer.class);
transient private static final long TIMEOUT = 5000;
public enum State {
AVAILABLE,
RESERVED,
CLOSED,
CANCELED
}
private final Offer offer;
private State state = State.AVAILABLE;
transient private Timer timeoutTimer;
transient private Storage<TradableList<OpenOffer>> storage;
public OpenOffer(Offer offer, Storage<TradableList<OpenOffer>> storage) {
this.offer = offer;
this.storage = storage;
}
public Date getDate() {
return offer.getCreationDate();
}
@Override
public String getId() {
return offer.getId();
}
public Offer getOffer() {
return offer;
}
public void setStorage(Storage<TradableList<OpenOffer>> storage) {
this.storage = storage;
}
public void setState(State state) {
log.trace("setState" + state);
this.state = state;
storage.queueUpForSave();
// We keep it reserved for a limited time, if trade preparation fails we revert to available state
if (this.state == State.RESERVED)
startTimeout();
else
stopTimeout();
}
public State getState() {
return state;
}
private void startTimeout() {
log.trace("startTimeout");
stopTimeout();
timeoutTimer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
Threading.USER_THREAD.execute(() -> {
log.debug("Timeout reached");
if (state == State.RESERVED)
setState(State.AVAILABLE);
});
}
};
timeoutTimer.schedule(task, TIMEOUT);
}
protected void stopTimeout() {
log.trace("stopTimeout");
if (timeoutTimer != null) {
timeoutTimer.cancel();
timeoutTimer = null;
}
}
}

View file

@ -0,0 +1,287 @@
/*
* 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.offer;
import io.bitsquare.btc.TradeWalletService;
import io.bitsquare.btc.WalletService;
import io.bitsquare.common.handlers.ErrorMessageHandler;
import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.crypto.KeyRing;
import io.bitsquare.crypto.MessageWithPubKey;
import io.bitsquare.fiat.FiatAccount;
import io.bitsquare.p2p.DecryptedMessageHandler;
import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.MessageService;
import io.bitsquare.p2p.Peer;
import io.bitsquare.p2p.listener.SendMessageListener;
import io.bitsquare.storage.Storage;
import io.bitsquare.trade.TradableList;
import io.bitsquare.trade.closed.ClosedTradableManager;
import io.bitsquare.trade.handlers.TransactionResultHandler;
import io.bitsquare.trade.protocol.availability.messages.OfferAvailabilityRequest;
import io.bitsquare.trade.protocol.availability.messages.OfferAvailabilityResponse;
import io.bitsquare.trade.protocol.placeoffer.PlaceOfferModel;
import io.bitsquare.trade.protocol.placeoffer.PlaceOfferProtocol;
import io.bitsquare.user.AccountSettings;
import io.bitsquare.user.User;
import org.bitcoinj.core.Coin;
import org.bitcoinj.utils.Fiat;
import com.google.inject.Inject;
import java.io.File;
import java.util.Optional;
import javax.inject.Named;
import javafx.collections.ObservableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.google.inject.internal.util.$Preconditions.checkNotNull;
import static io.bitsquare.util.Validator.nonEmptyStringOf;
public class OpenOfferManager {
private static final Logger log = LoggerFactory.getLogger(OpenOfferManager.class);
private final User user;
private final KeyRing keyRing;
private final AccountSettings accountSettings;
private final WalletService walletService;
private MessageService messageService;
private final TradeWalletService tradeWalletService;
private final OfferBookService offerBookService;
private ClosedTradableManager closedTradableManager;
private final TradableList<OpenOffer> openOffers;
private final Storage<TradableList<OpenOffer>> openOffersStorage;
private boolean shutDownRequested;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public OpenOfferManager(User user,
KeyRing keyRing,
AccountSettings accountSettings,
WalletService walletService,
MessageService messageService,
TradeWalletService tradeWalletService,
OfferBookService offerBookService,
ClosedTradableManager closedTradableManager,
@Named("storage.dir") File storageDir) {
this.user = user;
this.keyRing = keyRing;
this.accountSettings = accountSettings;
this.walletService = walletService;
this.messageService = messageService;
this.tradeWalletService = tradeWalletService;
this.offerBookService = offerBookService;
this.closedTradableManager = closedTradableManager;
openOffersStorage = new Storage<>(storageDir);
this.openOffers = new TradableList<>(openOffersStorage, "OpenOffers");
// In case the app did get killed the shutDown from the modules is not called, so we use a shutdown hook
Thread shutDownHookThread = new Thread(OpenOfferManager.this::shutDown, "OpenOfferManager.ShutDownHook");
Runtime.getRuntime().addShutdownHook(shutDownHookThread);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Lifecycle
///////////////////////////////////////////////////////////////////////////////////////////
public void onAllServicesInitialized() {
log.trace("onAllServicesInitialized");
// Handler for incoming offer availability requests
messageService.addDecryptedMessageHandler(new DecryptedMessageHandler() {
@Override
public void handleMessage(MessageWithPubKey messageWithPubKey, Peer sender) {
// We get an encrypted message but don't do the signature check as we don't know the peer yet.
// A basic sig check is in done also at decryption time
Message message = messageWithPubKey.getMessage();
if (message instanceof OfferAvailabilityRequest)
handleOfferAvailabilityRequest((OfferAvailabilityRequest) message, sender);
}
});
for (OpenOffer openOffer : openOffers) {
// We add own offers to offerbook when we go online again
offerBookService.addOffer(openOffer.getOffer(),
() -> log.debug("Successful added offer to DHT"),
(message, throwable) -> log.error("Add offer to DHT failed. " + message));
//setupDepositPublishedListener(openOffer);
openOffer.setStorage(openOffersStorage);
}
}
public void shutDown() {
if (!shutDownRequested) {
log.debug("shutDown");
shutDownRequested = true;
// we remove own offers form offerbook when we go offline
for (OpenOffer openOffer : openOffers) {
offerBookService.removeOfferAtShutDown(openOffer.getOffer());
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////////////////////
public void onPlaceOffer(String id,
Offer.Direction direction,
Fiat price,
Coin amount,
Coin minAmount,
TransactionResultHandler resultHandler,
ErrorMessageHandler errorMessageHandler) {
FiatAccount fiatAccount = user.currentFiatAccountProperty().get();
Offer offer = new Offer(id,
keyRing.getPubKeyRing(),
direction,
price.getValue(),
amount,
minAmount,
fiatAccount.type,
fiatAccount.currencyCode,
fiatAccount.country,
fiatAccount.id,
accountSettings.getAcceptedArbitratorIds(),
accountSettings.getSecurityDeposit(),
accountSettings.getAcceptedCountries(),
accountSettings.getAcceptedLanguageLocaleCodes());
PlaceOfferModel model = new PlaceOfferModel(offer, walletService, tradeWalletService, offerBookService);
PlaceOfferProtocol placeOfferProtocol = new PlaceOfferProtocol(
model,
transaction -> {
OpenOffer openOffer = new OpenOffer(offer, openOffersStorage);
openOffers.add(openOffer);
openOffersStorage.queueUpForSave();
resultHandler.handleResult(transaction);
},
errorMessageHandler::handleErrorMessage
);
placeOfferProtocol.placeOffer();
}
public void onCancelOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
Optional<OpenOffer> openOfferOptional = findOpenOffer(offer.getId());
if (openOfferOptional.isPresent())
onCancelOpenOffer(openOfferOptional.get(), resultHandler, errorMessageHandler);
}
public void onCancelOpenOffer(OpenOffer openOffer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
offerBookService.removeOffer(openOffer.getOffer(),
() -> {
openOffer.getOffer().setState(Offer.State.REMOVED);
openOffer.setState(OpenOffer.State.CANCELED);
openOffers.remove(openOffer);
closedTradableManager.add(openOffer);
//disposeCheckOfferAvailabilityRequest(offer);
resultHandler.handleResult();
},
(message, throwable) -> errorMessageHandler.handleErrorMessage(message));
}
public void reserveOpenOffer(OpenOffer openOffer) {
openOffer.setState(OpenOffer.State.RESERVED);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getters
///////////////////////////////////////////////////////////////////////////////////////////
public boolean isMyOffer(Offer offer) {
return offer.isMyOffer(keyRing);
}
public ObservableList<OpenOffer> getOpenOffers() {
return openOffers.getObservableList();
}
public Optional<OpenOffer> findOpenOffer(String offerId) {
return openOffers.stream().filter(openOffer -> openOffer.getOffer().getId().equals(offerId)).findAny();
}
// Close openOffer after deposit published
public void closeOpenOffer(Offer offer) {
findOpenOffer(offer.getId()).ifPresent(openOffer -> {
openOffers.remove(openOffer);
openOffer.setState(OpenOffer.State.CLOSED);
offerBookService.removeOffer(openOffer.getOffer(),
() -> log.trace("Successful removed offer"),
(message, throwable) -> log.error(message));
});
}
///////////////////////////////////////////////////////////////////////////////////////////
// Offer Availability
///////////////////////////////////////////////////////////////////////////////////////////
private void handleOfferAvailabilityRequest(OfferAvailabilityRequest message, Peer sender) {
log.trace("handleNewMessage: message = " + message.getClass().getSimpleName() + " from " + sender);
try {
nonEmptyStringOf(message.offerId);
checkNotNull(message.getPubKeyRing());
} catch (Throwable t) {
log.warn("Invalid message " + message.toString());
return;
}
Optional<OpenOffer> openOfferOptional = findOpenOffer(message.offerId);
boolean isAvailable = openOfferOptional.isPresent() && openOfferOptional.get().getState() == OpenOffer.State.AVAILABLE;
try {
OfferAvailabilityResponse offerAvailabilityResponse = new OfferAvailabilityResponse(message.offerId, isAvailable);
messageService.sendEncryptedMessage(sender,
message.getPubKeyRing(),
offerAvailabilityResponse,
new SendMessageListener() {
@Override
public void handleResult() {
log.trace("ReportOfferAvailabilityMessage successfully arrived at peer");
}
@Override
public void handleFault() {
log.info("Sending ReportOfferAvailabilityMessage failed.");
}
});
} catch (Throwable t) {
t.printStackTrace();
log.info("Exception at handleRequestIsOfferAvailableMessage " + t.getMessage());
}
}
}

View file

@ -15,15 +15,15 @@
* 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.offer.tomp2p; package io.bitsquare.trade.offer.tomp2p;
import io.bitsquare.common.handlers.FaultHandler; import io.bitsquare.common.handlers.FaultHandler;
import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.crypto.KeyRing; import io.bitsquare.crypto.KeyRing;
import io.bitsquare.offer.Offer;
import io.bitsquare.offer.OfferBookService;
import io.bitsquare.p2p.tomp2p.TomP2PDHTService; import io.bitsquare.p2p.tomp2p.TomP2PDHTService;
import io.bitsquare.p2p.tomp2p.TomP2PNode; import io.bitsquare.p2p.tomp2p.TomP2PNode;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.offer.OfferBookService;
import java.io.IOException; import java.io.IOException;

View file

@ -15,10 +15,10 @@
* 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.offer.tomp2p; package io.bitsquare.trade.offer.tomp2p;
import io.bitsquare.offer.OfferBookService; import io.bitsquare.trade.offer.OfferBookService;
import io.bitsquare.offer.OfferModule; import io.bitsquare.trade.offer.OfferModule;
import com.google.inject.Singleton; import com.google.inject.Singleton;

View file

@ -19,17 +19,17 @@ package io.bitsquare.trade.protocol.availability;
import io.bitsquare.common.taskrunner.Model; import io.bitsquare.common.taskrunner.Model;
import io.bitsquare.crypto.PubKeyRing; import io.bitsquare.crypto.PubKeyRing;
import io.bitsquare.offer.Offer;
import io.bitsquare.p2p.AddressService; import io.bitsquare.p2p.AddressService;
import io.bitsquare.p2p.MessageService; import io.bitsquare.p2p.MessageService;
import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.Peer;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.protocol.availability.messages.OfferMessage; import io.bitsquare.trade.protocol.availability.messages.OfferMessage;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public class CheckOfferAvailabilityModel implements Model { public class OfferAvailabilityModel implements Model {
private static final Logger log = LoggerFactory.getLogger(CheckOfferAvailabilityModel.class); private static final Logger log = LoggerFactory.getLogger(OfferAvailabilityModel.class);
public final Offer offer; public final Offer offer;
private final PubKeyRing pubKeyRing; private final PubKeyRing pubKeyRing;
@ -39,7 +39,7 @@ public class CheckOfferAvailabilityModel implements Model {
private Peer peer; private Peer peer;
private OfferMessage message; private OfferMessage message;
public CheckOfferAvailabilityModel(Offer offer, PubKeyRing pubKeyRing, MessageService messageService, AddressService addressService) { public OfferAvailabilityModel(Offer offer, PubKeyRing pubKeyRing, MessageService messageService, AddressService addressService) {
this.offer = offer; this.offer = offer;
this.pubKeyRing = pubKeyRing; this.pubKeyRing = pubKeyRing;
this.messageService = messageService; this.messageService = messageService;
@ -64,12 +64,10 @@ public class CheckOfferAvailabilityModel implements Model {
@Override @Override
public void persist() { public void persist() {
} }
@Override @Override
public void onComplete() { public void onComplete() {
} }
public PubKeyRing getPubKeyRing() { public PubKeyRing getPubKeyRing() {

View file

@ -21,15 +21,15 @@ import io.bitsquare.common.handlers.ErrorMessageHandler;
import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.common.taskrunner.TaskRunner; import io.bitsquare.common.taskrunner.TaskRunner;
import io.bitsquare.crypto.MessageWithPubKey; import io.bitsquare.crypto.MessageWithPubKey;
import io.bitsquare.offer.Offer;
import io.bitsquare.p2p.DecryptedMessageHandler; import io.bitsquare.p2p.DecryptedMessageHandler;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.Peer;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.protocol.availability.messages.OfferAvailabilityResponse;
import io.bitsquare.trade.protocol.availability.messages.OfferMessage; import io.bitsquare.trade.protocol.availability.messages.OfferMessage;
import io.bitsquare.trade.protocol.availability.messages.ReportOfferAvailabilityMessage;
import io.bitsquare.trade.protocol.availability.tasks.GetPeerAddress; import io.bitsquare.trade.protocol.availability.tasks.GetPeerAddress;
import io.bitsquare.trade.protocol.availability.tasks.ProcessReportOfferAvailabilityMessage; import io.bitsquare.trade.protocol.availability.tasks.ProcessOfferAvailabilityResponse;
import io.bitsquare.trade.protocol.availability.tasks.SendRequestIsOfferAvailableMessage; import io.bitsquare.trade.protocol.availability.tasks.SendOfferAvailabilityRequest;
import org.bitcoinj.utils.Threading; import org.bitcoinj.utils.Threading;
@ -41,26 +41,26 @@ import org.slf4j.LoggerFactory;
import static io.bitsquare.util.Validator.nonEmptyStringOf; import static io.bitsquare.util.Validator.nonEmptyStringOf;
public class CheckOfferAvailabilityProtocol { public class OfferAvailabilityProtocol {
private static final Logger log = LoggerFactory.getLogger(CheckOfferAvailabilityProtocol.class); private static final Logger log = LoggerFactory.getLogger(OfferAvailabilityProtocol.class);
private static final long TIMEOUT = 10000; private static final long TIMEOUT = 10000;
private final CheckOfferAvailabilityModel model; private final OfferAvailabilityModel model;
private final ResultHandler resultHandler; private final ResultHandler resultHandler;
private final ErrorMessageHandler errorMessageHandler; private final ErrorMessageHandler errorMessageHandler;
private final DecryptedMessageHandler decryptedMessageHandler; private final DecryptedMessageHandler decryptedMessageHandler;
private Timer timeoutTimer; private Timer timeoutTimer;
private boolean isCanceled; private boolean isCanceled;
private TaskRunner<CheckOfferAvailabilityModel> taskRunner; private TaskRunner<OfferAvailabilityModel> taskRunner;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Constructor // Constructor
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public CheckOfferAvailabilityProtocol(CheckOfferAvailabilityModel model, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { public OfferAvailabilityProtocol(OfferAvailabilityModel model, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
this.model = model; this.model = model;
this.resultHandler = resultHandler; this.resultHandler = resultHandler;
this.errorMessageHandler = errorMessageHandler; this.errorMessageHandler = errorMessageHandler;
@ -79,7 +79,7 @@ public class CheckOfferAvailabilityProtocol {
public void checkOfferAvailability() { public void checkOfferAvailability() {
// reset // reset
model.offer.setState(Offer.State.UNKNOWN); model.offer.setState(Offer.State.UNDEFINED);
model.messageService.addDecryptedMessageHandler(decryptedMessageHandler); model.messageService.addDecryptedMessageHandler(decryptedMessageHandler);
@ -89,7 +89,7 @@ public class CheckOfferAvailabilityProtocol {
); );
taskRunner.addTasks( taskRunner.addTasks(
GetPeerAddress.class, GetPeerAddress.class,
SendRequestIsOfferAvailableMessage.class SendOfferAvailabilityRequest.class
); );
startTimeout(); startTimeout();
taskRunner.run(); taskRunner.run();
@ -111,13 +111,13 @@ public class CheckOfferAvailabilityProtocol {
log.trace("handleNewMessage: message = " + message.getClass().getSimpleName() + " from " + sender); log.trace("handleNewMessage: message = " + message.getClass().getSimpleName() + " from " + sender);
if (message instanceof OfferMessage) { if (message instanceof OfferMessage) {
nonEmptyStringOf(((OfferMessage) message).offerId); nonEmptyStringOf(((OfferMessage) message).offerId);
if (message instanceof ReportOfferAvailabilityMessage && model.offer.getId().equals(((OfferMessage) message).offerId)) if (message instanceof OfferAvailabilityResponse && model.offer.getId().equals(((OfferMessage) message).offerId))
handleDecryptedMessage((ReportOfferAvailabilityMessage) message); handleDecryptedMessage((OfferAvailabilityResponse) message);
} }
} }
private void handleDecryptedMessage(ReportOfferAvailabilityMessage message) { private void handleDecryptedMessage(OfferAvailabilityResponse message) {
stopTimeout(); stopTimeout();
model.setMessage(message); model.setMessage(message);
@ -131,7 +131,7 @@ public class CheckOfferAvailabilityProtocol {
errorMessageHandler.handleErrorMessage(errorMessage); errorMessageHandler.handleErrorMessage(errorMessage);
} }
); );
taskRunner.addTasks(ProcessReportOfferAvailabilityMessage.class); taskRunner.addTasks(ProcessOfferAvailabilityResponse.class);
taskRunner.run(); taskRunner.run();
} }

View file

@ -18,22 +18,29 @@
package io.bitsquare.trade.protocol.availability.messages; package io.bitsquare.trade.protocol.availability.messages;
import io.bitsquare.crypto.PubKeyRing; import io.bitsquare.crypto.PubKeyRing;
import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
import java.io.Serializable; import java.io.Serializable;
public class RequestIsOfferAvailableMessage extends TradeMessage implements Serializable { public class OfferAvailabilityRequest extends OfferMessage implements Serializable {
// That object is sent over the wire, so we need to take care of version compatibility. // That object is sent over the wire, so we need to take care of version compatibility.
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final PubKeyRing pubKeyRing; private final PubKeyRing pubKeyRing;
public RequestIsOfferAvailableMessage(String tradeId, PubKeyRing pubKeyRing) { public OfferAvailabilityRequest(String offerId, PubKeyRing pubKeyRing) {
super(tradeId); super(offerId);
this.pubKeyRing = pubKeyRing; this.pubKeyRing = pubKeyRing;
} }
public PubKeyRing getPubKeyRing() { public PubKeyRing getPubKeyRing() {
return pubKeyRing; return pubKeyRing;
} }
@Override
public String toString() {
return "RequestIsOfferAvailableMessage{" +
"offerId=" + offerId +
"pubKeyRing=" + pubKeyRing != null ? pubKeyRing.toString() : "null" +
'}';
}
} }

View file

@ -19,14 +19,14 @@ package io.bitsquare.trade.protocol.availability.messages;
import java.io.Serializable; import java.io.Serializable;
public class ReportOfferAvailabilityMessage extends OfferMessage implements Serializable { public class OfferAvailabilityResponse extends OfferMessage implements Serializable {
// That object is sent over the wire, so we need to take care of version compatibility. // That object is sent over the wire, so we need to take care of version compatibility.
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public final boolean isOfferOpen; public final boolean isAvailable;
public ReportOfferAvailabilityMessage(String offerId, boolean isOfferOpen) { public OfferAvailabilityResponse(String offerId, boolean isAvailable) {
super(offerId); super(offerId);
this.isOfferOpen = isOfferOpen; this.isAvailable = isAvailable;
} }
} }

View file

@ -19,18 +19,18 @@ package io.bitsquare.trade.protocol.availability.tasks;
import io.bitsquare.common.taskrunner.Task; import io.bitsquare.common.taskrunner.Task;
import io.bitsquare.common.taskrunner.TaskRunner; import io.bitsquare.common.taskrunner.TaskRunner;
import io.bitsquare.offer.Offer;
import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.Peer;
import io.bitsquare.p2p.listener.GetPeerAddressListener; import io.bitsquare.p2p.listener.GetPeerAddressListener;
import io.bitsquare.trade.protocol.availability.CheckOfferAvailabilityModel; import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.protocol.availability.OfferAvailabilityModel;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public class GetPeerAddress extends Task<CheckOfferAvailabilityModel> { public class GetPeerAddress extends Task<OfferAvailabilityModel> {
private static final Logger log = LoggerFactory.getLogger(GetPeerAddress.class); private static final Logger log = LoggerFactory.getLogger(GetPeerAddress.class);
public GetPeerAddress(TaskRunner taskHandler, CheckOfferAvailabilityModel model) { public GetPeerAddress(TaskRunner taskHandler, OfferAvailabilityModel model) {
super(taskHandler, model); super(taskHandler, model);
errorMessage = "DHT lookup for peer address failed. Maybe the offerer was offline for too long time."; errorMessage = "DHT lookup for peer address failed. Maybe the offerer was offline for too long time.";
@ -55,8 +55,6 @@ public class GetPeerAddress extends Task<CheckOfferAvailabilityModel> {
} }
}); });
} catch (Throwable t) { } catch (Throwable t) {
model.offer.setState(Offer.State.FAULT);
failed(t); failed(t);
} }
} }

View file

@ -19,36 +19,34 @@ package io.bitsquare.trade.protocol.availability.tasks;
import io.bitsquare.common.taskrunner.Task; import io.bitsquare.common.taskrunner.Task;
import io.bitsquare.common.taskrunner.TaskRunner; import io.bitsquare.common.taskrunner.TaskRunner;
import io.bitsquare.offer.Offer; import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.protocol.availability.CheckOfferAvailabilityModel; import io.bitsquare.trade.protocol.availability.OfferAvailabilityModel;
import io.bitsquare.trade.protocol.availability.messages.ReportOfferAvailabilityMessage; import io.bitsquare.trade.protocol.availability.messages.OfferAvailabilityResponse;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public class ProcessReportOfferAvailabilityMessage extends Task<CheckOfferAvailabilityModel> { public class ProcessOfferAvailabilityResponse extends Task<OfferAvailabilityModel> {
private static final Logger log = LoggerFactory.getLogger(ProcessReportOfferAvailabilityMessage.class); private static final Logger log = LoggerFactory.getLogger(ProcessOfferAvailabilityResponse.class);
public ProcessReportOfferAvailabilityMessage(TaskRunner taskHandler, CheckOfferAvailabilityModel model) { public ProcessOfferAvailabilityResponse(TaskRunner taskHandler, OfferAvailabilityModel model) {
super(taskHandler, model); super(taskHandler, model);
} }
@Override @Override
protected void doRun() { protected void doRun() {
try { try {
ReportOfferAvailabilityMessage reportOfferAvailabilityMessage = (ReportOfferAvailabilityMessage) model.getMessage(); OfferAvailabilityResponse offerAvailabilityResponse = (OfferAvailabilityResponse) model.getMessage();
if (model.offer.getState() != Offer.State.REMOVED) { if (model.offer.getState() != Offer.State.REMOVED) {
if (reportOfferAvailabilityMessage.isOfferOpen) if (offerAvailabilityResponse.isAvailable)
model.offer.setState(Offer.State.AVAILABLE); model.offer.setState(Offer.State.AVAILABLE);
else else
model.offer.setState(Offer.State.RESERVED); model.offer.setState(Offer.State.NOT_AVAILABLE);
} }
complete(); complete();
} catch (Throwable t) { } catch (Throwable t) {
model.offer.setState(Offer.State.FAULT);
failed(t); failed(t);
} }
} }

View file

@ -19,26 +19,25 @@ package io.bitsquare.trade.protocol.availability.tasks;
import io.bitsquare.common.taskrunner.Task; import io.bitsquare.common.taskrunner.Task;
import io.bitsquare.common.taskrunner.TaskRunner; import io.bitsquare.common.taskrunner.TaskRunner;
import io.bitsquare.offer.Offer;
import io.bitsquare.p2p.listener.SendMessageListener; import io.bitsquare.p2p.listener.SendMessageListener;
import io.bitsquare.trade.protocol.availability.CheckOfferAvailabilityModel; import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.protocol.availability.messages.RequestIsOfferAvailableMessage; import io.bitsquare.trade.protocol.availability.OfferAvailabilityModel;
import io.bitsquare.trade.protocol.availability.messages.OfferAvailabilityRequest;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public class SendRequestIsOfferAvailableMessage extends Task<CheckOfferAvailabilityModel> { public class SendOfferAvailabilityRequest extends Task<OfferAvailabilityModel> {
private static final Logger log = LoggerFactory.getLogger(SendRequestIsOfferAvailableMessage.class); private static final Logger log = LoggerFactory.getLogger(SendOfferAvailabilityRequest.class);
private static final long serialVersionUID = 1L;
public SendRequestIsOfferAvailableMessage(TaskRunner taskHandler, CheckOfferAvailabilityModel model) { public SendOfferAvailabilityRequest(TaskRunner taskHandler, OfferAvailabilityModel model) {
super(taskHandler, model); super(taskHandler, model);
} }
@Override @Override
protected void doRun() { protected void doRun() {
try { try {
RequestIsOfferAvailableMessage message = new RequestIsOfferAvailableMessage(model.offer.getId(), model.getPubKeyRing()); OfferAvailabilityRequest message = new OfferAvailabilityRequest(model.offer.getId(), model.getPubKeyRing());
model.messageService.sendEncryptedMessage(model.getPeer(), model.messageService.sendEncryptedMessage(model.getPeer(),
model.offer.getPubKeyRing(), model.offer.getPubKeyRing(),
message, message,
@ -56,8 +55,6 @@ public class SendRequestIsOfferAvailableMessage extends Task<CheckOfferAvailabil
} }
}); });
} catch (Throwable t) { } catch (Throwable t) {
model.offer.setState(Offer.State.FAULT);
failed(t); failed(t);
} }
} }

View file

@ -20,8 +20,8 @@ package io.bitsquare.trade.protocol.placeoffer;
import io.bitsquare.btc.TradeWalletService; import io.bitsquare.btc.TradeWalletService;
import io.bitsquare.btc.WalletService; import io.bitsquare.btc.WalletService;
import io.bitsquare.common.taskrunner.Model; import io.bitsquare.common.taskrunner.Model;
import io.bitsquare.offer.Offer; import io.bitsquare.trade.offer.Offer;
import io.bitsquare.offer.OfferBookService; import io.bitsquare.trade.offer.OfferBookService;
import org.bitcoinj.core.Transaction; import org.bitcoinj.core.Transaction;

View file

@ -19,12 +19,8 @@ package io.bitsquare.trade.protocol.trade;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.Peer;
import io.bitsquare.p2p.listener.SendMessageListener;
import io.bitsquare.trade.BuyerAsOffererTrade; import io.bitsquare.trade.BuyerAsOffererTrade;
import io.bitsquare.trade.Trade; import io.bitsquare.trade.Trade;
import io.bitsquare.trade.protocol.availability.messages.ReportOfferAvailabilityMessage;
import io.bitsquare.trade.protocol.availability.messages.RequestIsOfferAvailableMessage;
import io.bitsquare.trade.protocol.trade.messages.RequestDepositTxInputsMessage;
import io.bitsquare.trade.protocol.trade.messages.RequestFinalizePayoutTxMessage; import io.bitsquare.trade.protocol.trade.messages.RequestFinalizePayoutTxMessage;
import io.bitsquare.trade.protocol.trade.messages.RequestPublishDepositTxMessage; import io.bitsquare.trade.protocol.trade.messages.RequestPublishDepositTxMessage;
import io.bitsquare.trade.protocol.trade.messages.TradeMessage; import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
@ -43,7 +39,6 @@ import io.bitsquare.trade.protocol.trade.tasks.offerer.VerifyTakeOfferFeePayment
import io.bitsquare.trade.protocol.trade.tasks.offerer.VerifyTakerAccount; import io.bitsquare.trade.protocol.trade.tasks.offerer.VerifyTakerAccount;
import io.bitsquare.trade.protocol.trade.tasks.shared.CommitPayoutTx; import io.bitsquare.trade.protocol.trade.tasks.shared.CommitPayoutTx;
import io.bitsquare.trade.protocol.trade.tasks.shared.SetupPayoutTxLockTimeReachedListener; import io.bitsquare.trade.protocol.trade.tasks.shared.SetupPayoutTxLockTimeReachedListener;
import io.bitsquare.trade.states.OffererTradeState;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -72,6 +67,26 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc
// Public methods // Public methods
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void handleTakeOfferRequest(TradeMessage message, Peer taker) {
checkTradeId(processModel.getId(), message);
processModel.setTradeMessage(message);
buyerAsOffererTrade.setTradingPeer(taker);
//buyerAsOffererTrade.setLifeCycleState(OffererTradeState.LifeCycleState.OFFER_RESERVED);
TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsOffererTrade,
() -> log.debug("taskRunner at handleRequestDepositTxInputsMessage completed"),
this::handleTaskRunnerFault);
taskRunner.addTasks(
ProcessRequestDepositTxInputsMessage.class,
CreateDepositTxInputs.class,
SendRequestPayDepositMessage.class
);
taskRunner.run();
startTimeout();
}
@Override @Override
public void doApplyMailboxMessage(Message message, Trade trade) { public void doApplyMailboxMessage(Message message, Trade trade) {
this.trade = trade; this.trade = trade;
@ -93,61 +108,6 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc
// Incoming message handling // Incoming message handling
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// OpenOffer requests
// We get an encrypted message but don't do the signature check as we don't know the peer yet.
// A basic sig check is in done also at decryption time
private void handle(RequestIsOfferAvailableMessage message, Peer sender) {
try {
checkTradeId(processModel.getId(), message);
// We don't store anything in the offererTradeProcessModel as we might be in a trade process and receive that request from another peer who wants
// to take the offer at the same time
boolean isOfferOpen = buyerAsOffererTrade.lifeCycleStateProperty().get() == OffererTradeState.LifeCycleState.OFFER_OPEN;
ReportOfferAvailabilityMessage reportOfferAvailabilityMessage = new ReportOfferAvailabilityMessage(processModel.getId(), isOfferOpen);
processModel.getMessageService().sendEncryptedMessage(sender,
message.getPubKeyRing(),
reportOfferAvailabilityMessage,
new SendMessageListener() {
@Override
public void handleResult() {
// Offerer does not do anything at that moment. Peer might only watch the offer and does not start a trade.
log.trace("ReportOfferAvailabilityMessage successfully arrived at peer");
}
@Override
public void handleFault() {
// We don't handle the error as we might be in a trade process with another trader
log.warn("Sending ReportOfferAvailabilityMessage failed.");
}
});
} catch (Throwable t) {
// We don't handle the error as we might be in a trade process with another trader
t.printStackTrace();
log.warn("Exception at handleRequestIsOfferAvailableMessage " + t.getMessage());
}
}
// Trade started. We reserve the offer for that taker. If anything goes wrong we reset the offer as open.
private void handle(RequestDepositTxInputsMessage tradeMessage, Peer taker) {
checkTradeId(processModel.getId(), tradeMessage);
processModel.setTradeMessage(tradeMessage);
buyerAsOffererTrade.setTradingPeer(taker);
buyerAsOffererTrade.setLifeCycleState(OffererTradeState.LifeCycleState.OFFER_RESERVED);
TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsOffererTrade,
() -> log.debug("taskRunner at handleRequestDepositTxInputsMessage completed"),
this::handleTaskRunnerFault);
taskRunner.addTasks(
ProcessRequestDepositTxInputsMessage.class,
CreateDepositTxInputs.class,
SendRequestPayDepositMessage.class
);
taskRunner.run();
startTimeout();
}
private void handle(RequestPublishDepositTxMessage tradeMessage) { private void handle(RequestPublishDepositTxMessage tradeMessage) {
stopTimeout(); stopTimeout();
processModel.setTradeMessage(tradeMessage); processModel.setTradeMessage(tradeMessage);
@ -217,13 +177,7 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc
@Override @Override
protected void doHandleDecryptedMessage(TradeMessage tradeMessage, Peer sender) { protected void doHandleDecryptedMessage(TradeMessage tradeMessage, Peer sender) {
if (tradeMessage instanceof RequestIsOfferAvailableMessage) { if (tradeMessage instanceof RequestPublishDepositTxMessage) {
handle((RequestIsOfferAvailableMessage) tradeMessage, sender);
}
else if (tradeMessage instanceof RequestDepositTxInputsMessage) {
handle((RequestDepositTxInputsMessage) tradeMessage, sender);
}
else if (tradeMessage instanceof RequestPublishDepositTxMessage) {
handle((RequestPublishDepositTxMessage) tradeMessage); handle((RequestPublishDepositTxMessage) tradeMessage);
} }
else if (tradeMessage instanceof RequestFinalizePayoutTxMessage) { else if (tradeMessage instanceof RequestFinalizePayoutTxMessage) {

View file

@ -17,5 +17,9 @@
package io.bitsquare.trade.protocol.trade; package io.bitsquare.trade.protocol.trade;
import io.bitsquare.p2p.Peer;
import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
public interface OffererProtocol { public interface OffererProtocol {
void handleTakeOfferRequest(TradeMessage message, Peer taker);
} }

View file

@ -27,9 +27,9 @@ import io.bitsquare.crypto.CryptoService;
import io.bitsquare.crypto.KeyRing; import io.bitsquare.crypto.KeyRing;
import io.bitsquare.crypto.PubKeyRing; import io.bitsquare.crypto.PubKeyRing;
import io.bitsquare.fiat.FiatAccount; import io.bitsquare.fiat.FiatAccount;
import io.bitsquare.offer.Offer;
import io.bitsquare.p2p.AddressService; import io.bitsquare.p2p.AddressService;
import io.bitsquare.p2p.MessageService; import io.bitsquare.p2p.MessageService;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.protocol.trade.messages.TradeMessage; import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
import io.bitsquare.user.User; import io.bitsquare.user.User;

View file

@ -19,15 +19,11 @@ package io.bitsquare.trade.protocol.trade;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.Peer;
import io.bitsquare.p2p.listener.SendMessageListener;
import io.bitsquare.trade.SellerAsOffererTrade; import io.bitsquare.trade.SellerAsOffererTrade;
import io.bitsquare.trade.Trade; import io.bitsquare.trade.Trade;
import io.bitsquare.trade.protocol.availability.messages.ReportOfferAvailabilityMessage;
import io.bitsquare.trade.protocol.availability.messages.RequestIsOfferAvailableMessage;
import io.bitsquare.trade.protocol.trade.messages.DepositTxPublishedMessage; import io.bitsquare.trade.protocol.trade.messages.DepositTxPublishedMessage;
import io.bitsquare.trade.protocol.trade.messages.FiatTransferStartedMessage; import io.bitsquare.trade.protocol.trade.messages.FiatTransferStartedMessage;
import io.bitsquare.trade.protocol.trade.messages.PayoutTxFinalizedMessage; import io.bitsquare.trade.protocol.trade.messages.PayoutTxFinalizedMessage;
import io.bitsquare.trade.protocol.trade.messages.RequestPayDepositMessage;
import io.bitsquare.trade.protocol.trade.messages.TradeMessage; import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
import io.bitsquare.trade.protocol.trade.tasks.offerer.VerifyTakeOfferFeePayment; import io.bitsquare.trade.protocol.trade.tasks.offerer.VerifyTakeOfferFeePayment;
import io.bitsquare.trade.protocol.trade.tasks.offerer.VerifyTakerAccount; import io.bitsquare.trade.protocol.trade.tasks.offerer.VerifyTakerAccount;
@ -43,13 +39,10 @@ import io.bitsquare.trade.protocol.trade.tasks.seller.SendRequestPublishDepositT
import io.bitsquare.trade.protocol.trade.tasks.seller.SignPayoutTx; import io.bitsquare.trade.protocol.trade.tasks.seller.SignPayoutTx;
import io.bitsquare.trade.protocol.trade.tasks.shared.CommitPayoutTx; import io.bitsquare.trade.protocol.trade.tasks.shared.CommitPayoutTx;
import io.bitsquare.trade.protocol.trade.tasks.shared.SetupPayoutTxLockTimeReachedListener; import io.bitsquare.trade.protocol.trade.tasks.shared.SetupPayoutTxLockTimeReachedListener;
import io.bitsquare.trade.states.OffererTradeState;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import static io.bitsquare.util.Validator.checkTradeId;
public class SellerAsOffererProtocol extends TradeProtocol implements SellerProtocol, OffererProtocol { public class SellerAsOffererProtocol extends TradeProtocol implements SellerProtocol, OffererProtocol {
private static final Logger log = LoggerFactory.getLogger(SellerAsOffererProtocol.class); private static final Logger log = LoggerFactory.getLogger(SellerAsOffererProtocol.class);
@ -72,6 +65,26 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
// Public methods // Public methods
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void handleTakeOfferRequest(TradeMessage message, Peer taker) {
processModel.setTradeMessage(message);
sellerAsOffererTrade.setTradingPeer(taker);
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsOffererTrade,
() -> log.debug("taskRunner at handleTakerDepositPaymentRequestMessage completed"),
this::handleTaskRunnerFault);
taskRunner.addTasks(
ProcessRequestPayDepositMessage.class,
VerifyTakerAccount.class,
CreateAndSignContract.class,
CreateAndSignDepositTx.class,
SendRequestPublishDepositTxMessage.class
);
taskRunner.run();
startTimeout();
}
@Override @Override
public void doApplyMailboxMessage(Message message, Trade trade) { public void doApplyMailboxMessage(Message message, Trade trade) {
this.trade = trade; this.trade = trade;
@ -101,67 +114,6 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
// Incoming message handling // Incoming message handling
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
// IsOfferAvailable
///////////////////////////////////////////////////////////////////////////////////////////
private void handle(RequestIsOfferAvailableMessage message, Peer sender) {
try {
checkTradeId(processModel.getId(), message);
// We don't store anything in the offererTradeProcessModel as we might be in a trade process and receive that request from another peer who wants
// to take the offer at the same time
boolean isOfferOpen = sellerAsOffererTrade.lifeCycleStateProperty().get() == OffererTradeState.LifeCycleState.OFFER_OPEN;
ReportOfferAvailabilityMessage reportOfferAvailabilityMessage = new ReportOfferAvailabilityMessage(processModel.getId(), isOfferOpen);
processModel.getMessageService().sendEncryptedMessage(sender,
message.getPubKeyRing(),
reportOfferAvailabilityMessage,
new SendMessageListener() {
@Override
public void handleResult() {
// Offerer does not do anything at that moment. Peer might only watch the offer and does not start a trade.
log.trace("ReportOfferAvailabilityMessage successfully arrived at peer");
}
@Override
public void handleFault() {
// We don't handle the error as we might be in a trade process with another trader
log.warn("Sending ReportOfferAvailabilityMessage failed.");
}
});
} catch (Throwable t) {
// We don't handle the error as we might be in a trade process with another trader
t.printStackTrace();
log.warn("Exception at handleRequestIsOfferAvailableMessage " + t.getMessage());
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// Trade
///////////////////////////////////////////////////////////////////////////////////////////
private void handle(RequestPayDepositMessage tradeMessage, Peer sender) {
processModel.setTradeMessage(tradeMessage);
sellerAsOffererTrade.setTradingPeer(sender);
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsOffererTrade,
() -> log.debug("taskRunner at handleTakerDepositPaymentRequestMessage completed"),
this::handleTaskRunnerFault);
taskRunner.addTasks(
ProcessRequestPayDepositMessage.class,
VerifyTakerAccount.class,
CreateAndSignContract.class,
CreateAndSignDepositTx.class,
SendRequestPublishDepositTxMessage.class
);
taskRunner.run();
startTimeout();
}
private void handle(DepositTxPublishedMessage tradeMessage) { private void handle(DepositTxPublishedMessage tradeMessage) {
stopTimeout(); stopTimeout();
processModel.setTradeMessage(tradeMessage); processModel.setTradeMessage(tradeMessage);
@ -239,13 +191,7 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
@Override @Override
protected void doHandleDecryptedMessage(TradeMessage tradeMessage, Peer sender) { protected void doHandleDecryptedMessage(TradeMessage tradeMessage, Peer sender) {
if (tradeMessage instanceof RequestIsOfferAvailableMessage) { if (tradeMessage instanceof DepositTxPublishedMessage) {
handle((RequestIsOfferAvailableMessage) tradeMessage, sender);
}
else if (tradeMessage instanceof RequestPayDepositMessage) {
handle((RequestPayDepositMessage) tradeMessage, sender);
}
else if (tradeMessage instanceof DepositTxPublishedMessage) {
handle((DepositTxPublishedMessage) tradeMessage); handle((DepositTxPublishedMessage) tradeMessage);
} }
else if (tradeMessage instanceof FiatTransferStartedMessage) { else if (tradeMessage instanceof FiatTransferStartedMessage) {

View file

@ -37,6 +37,7 @@ public class RequestPayDepositMessage extends TradeMessage implements Serializab
public final List<TransactionOutput> buyerConnectedOutputsForAllInputs; public final List<TransactionOutput> buyerConnectedOutputsForAllInputs;
public final List<TransactionOutput> buyerOutputs; public final List<TransactionOutput> buyerOutputs;
public final byte[] buyerTradeWalletPubKey; public final byte[] buyerTradeWalletPubKey;
public final boolean isInitialRequest;
public final PubKeyRing buyerPubKeyRing; public final PubKeyRing buyerPubKeyRing;
public final FiatAccount buyerFiatAccount; public final FiatAccount buyerFiatAccount;
public final String buyerAccountId; public final String buyerAccountId;
@ -44,6 +45,7 @@ public class RequestPayDepositMessage extends TradeMessage implements Serializab
public RequestPayDepositMessage(String tradeId, public RequestPayDepositMessage(String tradeId,
Coin tradeAmount, Coin tradeAmount,
boolean isInitialRequest,
List<TransactionOutput> buyerConnectedOutputsForAllInputs, List<TransactionOutput> buyerConnectedOutputsForAllInputs,
List<TransactionOutput> buyerOutputs, List<TransactionOutput> buyerOutputs,
byte[] buyerTradeWalletPubKey, byte[] buyerTradeWalletPubKey,
@ -52,6 +54,7 @@ public class RequestPayDepositMessage extends TradeMessage implements Serializab
String buyerAccountId) { String buyerAccountId) {
super(tradeId); super(tradeId);
this.tradeAmount = tradeAmount; this.tradeAmount = tradeAmount;
this.isInitialRequest = isInitialRequest;
this.buyerPubKeyRing = buyerPubKeyRing; this.buyerPubKeyRing = buyerPubKeyRing;
this.buyerConnectedOutputsForAllInputs = buyerConnectedOutputsForAllInputs; this.buyerConnectedOutputsForAllInputs = buyerConnectedOutputsForAllInputs;
this.buyerOutputs = buyerOutputs; this.buyerOutputs = buyerOutputs;

View file

@ -19,6 +19,7 @@ package io.bitsquare.trade.protocol.trade.tasks.buyer;
import io.bitsquare.common.taskrunner.TaskRunner; import io.bitsquare.common.taskrunner.TaskRunner;
import io.bitsquare.p2p.listener.SendMessageListener; import io.bitsquare.p2p.listener.SendMessageListener;
import io.bitsquare.trade.BuyerAsTakerTrade;
import io.bitsquare.trade.Trade; import io.bitsquare.trade.Trade;
import io.bitsquare.trade.protocol.trade.TradeTask; import io.bitsquare.trade.protocol.trade.TradeTask;
import io.bitsquare.trade.protocol.trade.messages.RequestPayDepositMessage; import io.bitsquare.trade.protocol.trade.messages.RequestPayDepositMessage;
@ -37,9 +38,11 @@ public class SendRequestPayDepositMessage extends TradeTask {
@Override @Override
protected void doRun() { protected void doRun() {
try { try {
boolean isInitialRequest = trade instanceof BuyerAsTakerTrade;
RequestPayDepositMessage tradeMessage = new RequestPayDepositMessage( RequestPayDepositMessage tradeMessage = new RequestPayDepositMessage(
processModel.getId(), processModel.getId(),
trade.getTradeAmount(), trade.getTradeAmount(),
isInitialRequest,
processModel.getConnectedOutputsForAllInputs(), processModel.getConnectedOutputsForAllInputs(),
processModel.getOutputs(), processModel.getOutputs(),
processModel.getTradeWalletPubKey(), processModel.getTradeWalletPubKey(),

View file

@ -66,14 +66,11 @@ public class SignAndPublishDepositTx extends TradeTask {
trade.setDepositTx(transaction); trade.setDepositTx(transaction);
if (trade instanceof TakerTrade) { trade.setLifeCycleState(Trade.LifeCycleState.PENDING);
if (trade instanceof TakerTrade)
trade.setProcessState(TakerTradeState.ProcessState.DEPOSIT_PUBLISHED); trade.setProcessState(TakerTradeState.ProcessState.DEPOSIT_PUBLISHED);
trade.setLifeCycleState(TakerTradeState.LifeCycleState.PENDING); else if (trade instanceof OffererTrade)
}
else if (trade instanceof OffererTrade) {
trade.setProcessState(OffererTradeState.ProcessState.DEPOSIT_PUBLISHED); trade.setProcessState(OffererTradeState.ProcessState.DEPOSIT_PUBLISHED);
trade.setLifeCycleState(OffererTradeState.LifeCycleState.PENDING);
}
trade.setTakeOfferDate(new Date()); trade.setTakeOfferDate(new Date());
@ -95,7 +92,7 @@ public class SignAndPublishDepositTx extends TradeTask {
trade.setThrowable(t); trade.setThrowable(t);
if (trade instanceof OffererTrade) if (trade instanceof OffererTrade)
trade.setLifeCycleState(OffererTradeState.LifeCycleState.OFFER_OPEN); trade.setLifeCycleState(Trade.LifeCycleState.PREPARATION);
failed(t); failed(t);
} }

View file

@ -23,15 +23,6 @@ import org.slf4j.LoggerFactory;
public class OffererTradeState { public class OffererTradeState {
private static final Logger log = LoggerFactory.getLogger(OffererTradeState.class); private static final Logger log = LoggerFactory.getLogger(OffererTradeState.class);
public enum LifeCycleState implements TradeState.LifeCycleState {
OFFER_OPEN,
OFFER_RESERVED,
OFFER_CANCELED,
PENDING,
COMPLETED,
FAILED
}
public enum ProcessState implements TradeState.ProcessState { public enum ProcessState implements TradeState.ProcessState {
UNDEFINED, UNDEFINED,
DEPOSIT_PUBLISHED, DEPOSIT_PUBLISHED,

View file

@ -36,6 +36,6 @@ public class StateUtil {
public static void setOfferOpenState(Trade trade) { public static void setOfferOpenState(Trade trade) {
if (trade instanceof OffererTrade) if (trade instanceof OffererTrade)
trade.setLifeCycleState(OffererTradeState.LifeCycleState.OFFER_OPEN); trade.setLifeCycleState(Trade.LifeCycleState.PREPARATION);
} }
} }

View file

@ -23,12 +23,6 @@ import org.slf4j.LoggerFactory;
public class TakerTradeState { public class TakerTradeState {
private static final Logger log = LoggerFactory.getLogger(TakerTradeState.class); private static final Logger log = LoggerFactory.getLogger(TakerTradeState.class);
public enum LifeCycleState implements TradeState.LifeCycleState {
PENDING,
COMPLETED,
FAILED
}
public enum ProcessState implements TradeState.ProcessState { public enum ProcessState implements TradeState.ProcessState {
UNDEFINED, UNDEFINED,
TAKE_OFFER_FEE_TX_CREATED, TAKE_OFFER_FEE_TX_CREATED,

View file

@ -23,9 +23,6 @@ import org.slf4j.LoggerFactory;
public class TradeState { public class TradeState {
private static final Logger log = LoggerFactory.getLogger(TradeState.class); private static final Logger log = LoggerFactory.getLogger(TradeState.class);
public interface LifeCycleState {
}
public interface ProcessState { public interface ProcessState {
} }
} }

View file

@ -18,10 +18,10 @@
package io.bitsquare.trade.protocol.placeoffer; package io.bitsquare.trade.protocol.placeoffer;
import io.bitsquare.btc.WalletService; import io.bitsquare.btc.WalletService;
import io.bitsquare.offer.OfferBookService;
import io.bitsquare.p2p.MessageService; import io.bitsquare.p2p.MessageService;
import io.bitsquare.p2p.tomp2p.BootstrappedPeerBuilder; import io.bitsquare.p2p.tomp2p.BootstrappedPeerBuilder;
import io.bitsquare.p2p.tomp2p.TomP2PNode; import io.bitsquare.p2p.tomp2p.TomP2PNode;
import io.bitsquare.trade.offer.OfferBookService;
import org.bitcoinj.core.Address; import org.bitcoinj.core.Address;

View file

@ -25,12 +25,12 @@ import io.bitsquare.crypto.CryptoModule;
import io.bitsquare.crypto.KeyRing; import io.bitsquare.crypto.KeyRing;
import io.bitsquare.crypto.KeyStorage; import io.bitsquare.crypto.KeyStorage;
import io.bitsquare.gui.GuiModule; import io.bitsquare.gui.GuiModule;
import io.bitsquare.offer.OfferModule;
import io.bitsquare.offer.tomp2p.TomP2POfferModule;
import io.bitsquare.p2p.P2PModule; import io.bitsquare.p2p.P2PModule;
import io.bitsquare.p2p.tomp2p.TomP2PModule; import io.bitsquare.p2p.tomp2p.TomP2PModule;
import io.bitsquare.storage.Storage; import io.bitsquare.storage.Storage;
import io.bitsquare.trade.TradeModule; import io.bitsquare.trade.TradeModule;
import io.bitsquare.trade.offer.OfferModule;
import io.bitsquare.trade.offer.tomp2p.TomP2POfferModule;
import io.bitsquare.user.AccountSettings; import io.bitsquare.user.AccountSettings;
import io.bitsquare.user.Preferences; import io.bitsquare.user.Preferences;
import io.bitsquare.user.User; import io.bitsquare.user.User;

View file

@ -31,6 +31,7 @@ import io.bitsquare.p2p.BootstrapState;
import io.bitsquare.p2p.ClientNode; import io.bitsquare.p2p.ClientNode;
import io.bitsquare.trade.Trade; import io.bitsquare.trade.Trade;
import io.bitsquare.trade.TradeManager; import io.bitsquare.trade.TradeManager;
import io.bitsquare.trade.offer.OpenOfferManager;
import io.bitsquare.user.User; import io.bitsquare.user.User;
import com.google.inject.Inject; import com.google.inject.Inject;
@ -91,12 +92,13 @@ class MainViewModel implements ViewModel {
private final ArbitrationRepository arbitrationRepository; private final ArbitrationRepository arbitrationRepository;
private final ClientNode clientNode; private final ClientNode clientNode;
private final TradeManager tradeManager; private final TradeManager tradeManager;
private OpenOfferManager openOfferManager;
private final UpdateProcess updateProcess; private final UpdateProcess updateProcess;
private final BSFormatter formatter; private final BSFormatter formatter;
@Inject @Inject
public MainViewModel(User user, KeyRing keyRing, WalletService walletService, ArbitrationRepository arbitrationRepository, ClientNode clientNode, public MainViewModel(User user, KeyRing keyRing, WalletService walletService, ArbitrationRepository arbitrationRepository, ClientNode clientNode,
TradeManager tradeManager, BitcoinNetwork bitcoinNetwork, UpdateProcess updateProcess, TradeManager tradeManager, OpenOfferManager openOfferManager, BitcoinNetwork bitcoinNetwork, UpdateProcess updateProcess,
BSFormatter formatter) { BSFormatter formatter) {
this.user = user; this.user = user;
this.keyRing = keyRing; this.keyRing = keyRing;
@ -104,6 +106,7 @@ class MainViewModel implements ViewModel {
this.arbitrationRepository = arbitrationRepository; this.arbitrationRepository = arbitrationRepository;
this.clientNode = clientNode; this.clientNode = clientNode;
this.tradeManager = tradeManager; this.tradeManager = tradeManager;
this.openOfferManager = openOfferManager;
this.updateProcess = updateProcess; this.updateProcess = updateProcess;
this.formatter = formatter; this.formatter = formatter;
@ -211,6 +214,7 @@ class MainViewModel implements ViewModel {
log.debug("loadAllArbitrators"); log.debug("loadAllArbitrators");
arbitrationRepository.loadAllArbitrators(); arbitrationRepository.loadAllArbitrators();
tradeManager.onAllServicesInitialized(); tradeManager.onAllServicesInitialized();
openOfferManager.onAllServicesInitialized();
} }
private void applyUpdateState(UpdateProcess.State state) { private void applyUpdateState(UpdateProcess.State state) {

View file

@ -20,9 +20,9 @@ package io.bitsquare.gui.main.debug;
import io.bitsquare.common.taskrunner.Task; import io.bitsquare.common.taskrunner.Task;
import io.bitsquare.gui.common.view.FxmlView; import io.bitsquare.gui.common.view.FxmlView;
import io.bitsquare.gui.common.view.InitializableView; import io.bitsquare.gui.common.view.InitializableView;
import io.bitsquare.trade.protocol.availability.CheckOfferAvailabilityProtocol; import io.bitsquare.trade.protocol.availability.OfferAvailabilityProtocol;
import io.bitsquare.trade.protocol.availability.tasks.ProcessReportOfferAvailabilityMessage; import io.bitsquare.trade.protocol.availability.tasks.ProcessOfferAvailabilityResponse;
import io.bitsquare.trade.protocol.availability.tasks.SendRequestIsOfferAvailableMessage; import io.bitsquare.trade.protocol.availability.tasks.SendOfferAvailabilityRequest;
import io.bitsquare.trade.protocol.placeoffer.PlaceOfferProtocol; import io.bitsquare.trade.protocol.placeoffer.PlaceOfferProtocol;
import io.bitsquare.trade.protocol.placeoffer.tasks.AddOfferToRemoteOfferBook; import io.bitsquare.trade.protocol.placeoffer.tasks.AddOfferToRemoteOfferBook;
import io.bitsquare.trade.protocol.placeoffer.tasks.BroadcastCreateOfferFeeTx; import io.bitsquare.trade.protocol.placeoffer.tasks.BroadcastCreateOfferFeeTx;
@ -80,10 +80,10 @@ public class DebugView extends InitializableView {
final ObservableList<Class> items = FXCollections.observableArrayList(Arrays.asList( final ObservableList<Class> items = FXCollections.observableArrayList(Arrays.asList(
/*---- Protocol ----*/ /*---- Protocol ----*/
CheckOfferAvailabilityProtocol.class, OfferAvailabilityProtocol.class,
io.bitsquare.trade.protocol.availability.tasks.GetPeerAddress.class, io.bitsquare.trade.protocol.availability.tasks.GetPeerAddress.class,
SendRequestIsOfferAvailableMessage.class, SendOfferAvailabilityRequest.class,
ProcessReportOfferAvailabilityMessage.class, ProcessOfferAvailabilityResponse.class,
Boolean.class, /* used as seperator*/ Boolean.class, /* used as seperator*/

View file

@ -24,6 +24,7 @@ import io.bitsquare.gui.common.view.FxmlView;
import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.gui.util.GUIUtil; import io.bitsquare.gui.util.GUIUtil;
import io.bitsquare.trade.TradeManager; import io.bitsquare.trade.TradeManager;
import io.bitsquare.trade.offer.OpenOfferManager;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
@ -51,13 +52,15 @@ public class ReservedView extends ActivatableViewAndModel {
private final WalletService walletService; private final WalletService walletService;
private final TradeManager tradeManager; private final TradeManager tradeManager;
private OpenOfferManager openOfferManager;
private final BSFormatter formatter; private final BSFormatter formatter;
private final ObservableList<ReservedListItem> addressList = FXCollections.observableArrayList(); private final ObservableList<ReservedListItem> addressList = FXCollections.observableArrayList();
@Inject @Inject
private ReservedView(WalletService walletService, TradeManager tradeManager, BSFormatter formatter) { private ReservedView(WalletService walletService, TradeManager tradeManager, OpenOfferManager openOfferManager, BSFormatter formatter) {
this.walletService = walletService; this.walletService = walletService;
this.tradeManager = tradeManager; this.tradeManager = tradeManager;
this.openOfferManager = openOfferManager;
this.formatter = formatter; this.formatter = formatter;
} }
@ -94,8 +97,8 @@ public class ReservedView extends ActivatableViewAndModel {
private void fillList() { private void fillList() {
addressList.clear(); addressList.clear();
addressList.addAll(Stream.concat(tradeManager.getOpenOfferTrades().stream(), tradeManager.getPendingTrades().stream()) addressList.addAll(Stream.concat(openOfferManager.getOpenOffers().stream(), tradeManager.getPendingTrades().stream())
.map(trade -> new ReservedListItem(walletService.getAddressEntry(trade.getId()), walletService, formatter)) .map(tradable -> new ReservedListItem(walletService.getAddressEntry(tradable.getOffer().getId()), walletService, formatter))
.collect(Collectors.toList())); .collect(Collectors.toList()));
// List<AddressEntry> addressEntryList = walletService.getAddressEntryList(); // List<AddressEntry> addressEntryList = walletService.getAddressEntryList();

View file

@ -28,6 +28,7 @@ import io.bitsquare.gui.components.Popups;
import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.gui.util.GUIUtil; import io.bitsquare.gui.util.GUIUtil;
import io.bitsquare.trade.TradeManager; import io.bitsquare.trade.TradeManager;
import io.bitsquare.trade.offer.OpenOfferManager;
import org.bitcoinj.core.AddressFormatException; import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
@ -67,13 +68,15 @@ public class WithdrawalView extends ActivatableViewAndModel {
private final WalletService walletService; private final WalletService walletService;
private TradeManager tradeManager; private TradeManager tradeManager;
private OpenOfferManager openOfferManager;
private final BSFormatter formatter; private final BSFormatter formatter;
private final ObservableList<WithdrawalListItem> addressList = FXCollections.observableArrayList(); private final ObservableList<WithdrawalListItem> addressList = FXCollections.observableArrayList();
@Inject @Inject
private WithdrawalView(WalletService walletService, TradeManager tradeManager, BSFormatter formatter) { private WithdrawalView(WalletService walletService, TradeManager tradeManager, OpenOfferManager openOfferManager, BSFormatter formatter) {
this.walletService = walletService; this.walletService = walletService;
this.tradeManager = tradeManager; this.tradeManager = tradeManager;
this.openOfferManager = openOfferManager;
this.formatter = formatter; this.formatter = formatter;
} }
@ -181,8 +184,8 @@ public class WithdrawalView extends ActivatableViewAndModel {
addressList.clear(); addressList.clear();
List<AddressEntry> addressEntryList = walletService.getAddressEntryList(); List<AddressEntry> addressEntryList = walletService.getAddressEntryList();
List<String> reservedTrades = Stream.concat(tradeManager.getOpenOfferTrades().stream(), tradeManager.getPendingTrades().stream()) List<String> reservedTrades = Stream.concat(openOfferManager.getOpenOffers().stream(), tradeManager.getPendingTrades().stream())
.map(trade -> trade.getId()) .map(tradable -> tradable.getOffer().getId())
.collect(Collectors.toList()); .collect(Collectors.toList());
addressList.addAll(addressEntryList.stream() addressList.addAll(addressEntryList.stream()

View file

@ -26,7 +26,7 @@ import io.bitsquare.gui.main.MainView;
import io.bitsquare.gui.main.offer.createoffer.CreateOfferView; import io.bitsquare.gui.main.offer.createoffer.CreateOfferView;
import io.bitsquare.gui.main.offer.offerbook.OfferBookView; import io.bitsquare.gui.main.offer.offerbook.OfferBookView;
import io.bitsquare.gui.main.offer.takeoffer.TakeOfferView; import io.bitsquare.gui.main.offer.takeoffer.TakeOfferView;
import io.bitsquare.offer.Offer; import io.bitsquare.trade.offer.Offer;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.bitcoinj.utils.Fiat; import org.bitcoinj.utils.Fiat;

View file

@ -28,8 +28,8 @@ import io.bitsquare.gui.common.model.Activatable;
import io.bitsquare.gui.common.model.DataModel; import io.bitsquare.gui.common.model.DataModel;
import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.locale.Country; import io.bitsquare.locale.Country;
import io.bitsquare.offer.Offer; import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.TradeManager; import io.bitsquare.trade.offer.OpenOfferManager;
import io.bitsquare.user.AccountSettings; import io.bitsquare.user.AccountSettings;
import io.bitsquare.user.Preferences; import io.bitsquare.user.Preferences;
import io.bitsquare.user.User; import io.bitsquare.user.User;
@ -67,7 +67,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
class CreateOfferDataModel implements Activatable, DataModel { class CreateOfferDataModel implements Activatable, DataModel {
private static final Logger log = LoggerFactory.getLogger(CreateOfferDataModel.class); private static final Logger log = LoggerFactory.getLogger(CreateOfferDataModel.class);
private final TradeManager tradeManager; private OpenOfferManager openOfferManager;
private final WalletService walletService; private final WalletService walletService;
private final AccountSettings accountSettings; private final AccountSettings accountSettings;
private final Preferences preferences; private final Preferences preferences;
@ -106,11 +106,10 @@ class CreateOfferDataModel implements Activatable, DataModel {
// non private for testing // non private for testing
@Inject @Inject
public CreateOfferDataModel(TradeManager tradeManager, WalletService walletService, ArbitratorService arbitratorService, public CreateOfferDataModel(OpenOfferManager openOfferManager, WalletService walletService, ArbitratorService arbitratorService,
AccountSettings accountSettings, Preferences preferences, User user, BSFormatter formatter) { AccountSettings accountSettings, Preferences preferences, User user, BSFormatter formatter) {
this.tradeManager = tradeManager; this.openOfferManager = openOfferManager;
this.walletService = walletService; this.walletService = walletService;
ArbitratorService arbitratorService1 = arbitratorService;
this.accountSettings = accountSettings; this.accountSettings = accountSettings;
this.preferences = preferences; this.preferences = preferences;
this.formatter = formatter; this.formatter = formatter;
@ -163,9 +162,9 @@ class CreateOfferDataModel implements Activatable, DataModel {
// no-op // no-op
} }
void placeOffer() { void onPlaceOffer() {
// data validation is done in the trade domain // data validation is done in the trade domain
tradeManager.placeOffer(offerId, openOfferManager.onPlaceOffer(offerId,
direction, direction,
priceAsFiat.get(), priceAsFiat.get(),
amountAsCoin.get(), amountAsCoin.get(),

View file

@ -38,7 +38,7 @@ import io.bitsquare.gui.main.portfolio.PortfolioView;
import io.bitsquare.gui.main.portfolio.openoffer.OpenOffersView; import io.bitsquare.gui.main.portfolio.openoffer.OpenOffersView;
import io.bitsquare.gui.util.ImageUtil; import io.bitsquare.gui.util.ImageUtil;
import io.bitsquare.locale.BSResources; import io.bitsquare.locale.BSResources;
import io.bitsquare.offer.Offer; import io.bitsquare.trade.offer.Offer;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.bitcoinj.utils.Fiat; import org.bitcoinj.utils.Fiat;
@ -141,7 +141,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
@FXML @FXML
void onPlaceOffer() { void onPlaceOffer() {
model.placeOffer(); model.onPlaceOffer();
} }
@FXML @FXML

View file

@ -25,7 +25,7 @@ import io.bitsquare.gui.util.validation.BtcValidator;
import io.bitsquare.gui.util.validation.FiatValidator; import io.bitsquare.gui.util.validation.FiatValidator;
import io.bitsquare.gui.util.validation.InputValidator; import io.bitsquare.gui.util.validation.InputValidator;
import io.bitsquare.locale.BSResources; import io.bitsquare.locale.BSResources;
import io.bitsquare.offer.Offer; import io.bitsquare.trade.offer.Offer;
import org.bitcoinj.core.Address; import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
@ -154,13 +154,13 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
} }
void placeOffer() { void onPlaceOffer() {
dataModel.requestPlaceOfferErrorMessage.set(null); dataModel.requestPlaceOfferErrorMessage.set(null);
dataModel.requestPlaceOfferSuccess.set(false); dataModel.requestPlaceOfferSuccess.set(false);
isPlaceOfferSpinnerVisible.set(true); isPlaceOfferSpinnerVisible.set(true);
dataModel.placeOffer(); dataModel.onPlaceOffer();
} }

View file

@ -21,9 +21,9 @@ import io.bitsquare.fiat.FiatAccount;
import io.bitsquare.gui.util.GUIUtil; import io.bitsquare.gui.util.GUIUtil;
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.OfferBookService;
import io.bitsquare.trade.TradeManager; import io.bitsquare.trade.TradeManager;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.offer.OfferBookService;
import io.bitsquare.user.User; import io.bitsquare.user.User;
import java.util.List; import java.util.List;
@ -95,7 +95,7 @@ public class OfferBook {
// Update state in case that that offer is used in the take offer screen, so it gets updated correctly // Update state in case that that offer is used in the take offer screen, so it gets updated correctly
offer.setState(Offer.State.REMOVED); offer.setState(Offer.State.REMOVED);
// clean up possible references in tradeManager // clean up possible references in openOfferManager
tradeManager.onOfferRemovedFromRemoteOfferBook(offer); tradeManager.onOfferRemovedFromRemoteOfferBook(offer);
offerBookListItems.removeIf(item -> item.getOffer().getId().equals(offer.getId())); offerBookListItems.removeIf(item -> item.getOffer().getId().equals(offer.getId()));

View file

@ -25,8 +25,8 @@ import io.bitsquare.gui.common.model.DataModel;
import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.gui.util.BSFormatter;
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.trade.offer.Offer;
import io.bitsquare.trade.TradeManager; import io.bitsquare.trade.offer.OpenOfferManager;
import io.bitsquare.user.Preferences; import io.bitsquare.user.Preferences;
import io.bitsquare.user.User; import io.bitsquare.user.User;
@ -58,7 +58,7 @@ class OfferBookDataModel implements Activatable, DataModel {
private final OfferBook offerBook; private final OfferBook offerBook;
private final Preferences preferences; private final Preferences preferences;
private final BSFormatter formatter; private final BSFormatter formatter;
private final TradeManager tradeManager; private final OpenOfferManager openOfferManager;
private final FilteredList<OfferBookListItem> filteredItems; private final FilteredList<OfferBookListItem> filteredItems;
private final SortedList<OfferBookListItem> sortedItems; private final SortedList<OfferBookListItem> sortedItems;
@ -77,9 +77,9 @@ class OfferBookDataModel implements Activatable, DataModel {
@Inject @Inject
public OfferBookDataModel(User user, TradeManager tradeManager, OfferBook offerBook, Preferences preferences, public OfferBookDataModel(User user, OpenOfferManager openOfferManager, OfferBook offerBook, Preferences preferences,
BSFormatter formatter) { BSFormatter formatter) {
this.tradeManager = tradeManager; this.openOfferManager = openOfferManager;
this.user = user; this.user = user;
this.offerBook = offerBook; this.offerBook = offerBook;
this.preferences = preferences; this.preferences = preferences;
@ -112,7 +112,7 @@ class OfferBookDataModel implements Activatable, DataModel {
} }
void onCancelOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { void onCancelOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
tradeManager.onCancelOpenOffer(offer, resultHandler, errorMessageHandler); openOfferManager.onCancelOpenOffer(offer, resultHandler, errorMessageHandler);
} }
void calculateVolume() { void calculateVolume() {
@ -210,7 +210,7 @@ class OfferBookDataModel implements Activatable, DataModel {
} }
boolean isMyOffer(Offer offer) { boolean isMyOffer(Offer offer) {
return tradeManager.isMyOffer(offer); return openOfferManager.isMyOffer(offer);
} }
Coin getAmountAsCoin() { Coin getAmountAsCoin() {

View file

@ -18,7 +18,7 @@
package io.bitsquare.gui.main.offer.offerbook; package io.bitsquare.gui.main.offer.offerbook;
import io.bitsquare.locale.Country; import io.bitsquare.locale.Country;
import io.bitsquare.offer.Offer; import io.bitsquare.trade.offer.Offer;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;

View file

@ -35,7 +35,7 @@ import io.bitsquare.gui.util.ImageUtil;
import io.bitsquare.gui.util.validation.OptionalBtcValidator; import io.bitsquare.gui.util.validation.OptionalBtcValidator;
import io.bitsquare.gui.util.validation.OptionalFiatValidator; import io.bitsquare.gui.util.validation.OptionalFiatValidator;
import io.bitsquare.locale.BSResources; import io.bitsquare.locale.BSResources;
import io.bitsquare.offer.Offer; import io.bitsquare.trade.offer.Offer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;

View file

@ -26,7 +26,7 @@ import io.bitsquare.gui.util.validation.InputValidator;
import io.bitsquare.gui.util.validation.OptionalBtcValidator; import io.bitsquare.gui.util.validation.OptionalBtcValidator;
import io.bitsquare.gui.util.validation.OptionalFiatValidator; import io.bitsquare.gui.util.validation.OptionalFiatValidator;
import io.bitsquare.locale.BSResources; import io.bitsquare.locale.BSResources;
import io.bitsquare.offer.Offer; import io.bitsquare.trade.offer.Offer;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.bitcoinj.utils.Fiat; import org.bitcoinj.utils.Fiat;

View file

@ -23,9 +23,9 @@ import io.bitsquare.btc.WalletService;
import io.bitsquare.btc.listeners.BalanceListener; import io.bitsquare.btc.listeners.BalanceListener;
import io.bitsquare.gui.common.model.Activatable; import io.bitsquare.gui.common.model.Activatable;
import io.bitsquare.gui.common.model.DataModel; import io.bitsquare.gui.common.model.DataModel;
import io.bitsquare.offer.Offer;
import io.bitsquare.trade.TradeManager; import io.bitsquare.trade.TradeManager;
import io.bitsquare.trade.handlers.TakeOfferResultHandler; import io.bitsquare.trade.handlers.TakeOfferResultHandler;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.user.Preferences; import io.bitsquare.user.Preferences;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;

View file

@ -36,7 +36,7 @@ import io.bitsquare.gui.main.portfolio.PortfolioView;
import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesView; import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesView;
import io.bitsquare.gui.util.ImageUtil; import io.bitsquare.gui.util.ImageUtil;
import io.bitsquare.locale.BSResources; import io.bitsquare.locale.BSResources;
import io.bitsquare.offer.Offer; import io.bitsquare.trade.offer.Offer;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;

View file

@ -25,9 +25,9 @@ import io.bitsquare.gui.util.validation.BtcValidator;
import io.bitsquare.gui.util.validation.InputValidator; import io.bitsquare.gui.util.validation.InputValidator;
import io.bitsquare.locale.BSResources; import io.bitsquare.locale.BSResources;
import io.bitsquare.locale.CurrencyUtil; import io.bitsquare.locale.CurrencyUtil;
import io.bitsquare.offer.Offer;
import io.bitsquare.trade.BuyerAsTakerTrade; import io.bitsquare.trade.BuyerAsTakerTrade;
import io.bitsquare.trade.SellerAsTakerTrade; import io.bitsquare.trade.SellerAsTakerTrade;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.states.TakerTradeState; import io.bitsquare.trade.states.TakerTradeState;
import org.bitcoinj.core.Address; import org.bitcoinj.core.Address;
@ -173,13 +173,13 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
log.debug("offer state = " + state); log.debug("offer state = " + state);
switch (state) { switch (state) {
case UNKNOWN: case UNDEFINED:
// TODO set spinner? // TODO set spinner?
break; break;
case AVAILABLE: case AVAILABLE:
this.state.set(State.AMOUNT_SCREEN); this.state.set(State.AMOUNT_SCREEN);
break; break;
case RESERVED: case NOT_AVAILABLE:
if (takeOfferRequested) if (takeOfferRequested)
errorMessage.set("Take offer request failed because offer is not available anymore. " + errorMessage.set("Take offer request failed because offer is not available anymore. " +
"Maybe another trader has taken the offer in the meantime."); "Maybe another trader has taken the offer in the meantime.");
@ -200,14 +200,14 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
errorMessage.set("You cannot take that offer because the offerer is offline."); errorMessage.set("You cannot take that offer because the offerer is offline.");
takeOfferRequested = false; takeOfferRequested = false;
break; break;
case FAULT: /* case FAULT:
if (takeOfferRequested) if (takeOfferRequested)
errorMessage.set("Take offer request failed."); errorMessage.set("Take offer request failed.");
else else
errorMessage.set("The check for the offer availability failed."); errorMessage.set("The check for the offer availability failed.");
takeOfferRequested = false; takeOfferRequested = false;
break; break;*/
default: default:
log.error("Unhandled offer state: " + state); log.error("Unhandled offer state: " + state);
break; break;

View file

@ -19,10 +19,9 @@ package io.bitsquare.gui.main.portfolio.closedtrades;
import io.bitsquare.gui.common.model.Activatable; import io.bitsquare.gui.common.model.Activatable;
import io.bitsquare.gui.common.model.DataModel; import io.bitsquare.gui.common.model.DataModel;
import io.bitsquare.offer.Offer; import io.bitsquare.trade.Tradable;
import io.bitsquare.trade.Trade; import io.bitsquare.trade.closed.ClosedTradableManager;
import io.bitsquare.trade.TradeManager; import io.bitsquare.trade.offer.Offer;
import io.bitsquare.user.User;
import com.google.inject.Inject; import com.google.inject.Inject;
@ -34,16 +33,14 @@ import javafx.collections.ObservableList;
class ClosedTradesDataModel implements Activatable, DataModel { class ClosedTradesDataModel implements Activatable, DataModel {
private final TradeManager tradeManager; private final ClosedTradableManager closedTradableManager;
private final User user;
private final ObservableList<ClosedTradesListItem> list = FXCollections.observableArrayList(); private final ObservableList<ClosedTradesListItem> list = FXCollections.observableArrayList();
private final ListChangeListener<Trade> tradesListChangeListener; private final ListChangeListener<Tradable> tradesListChangeListener;
@Inject @Inject
public ClosedTradesDataModel(TradeManager tradeManager, User user) { public ClosedTradesDataModel(ClosedTradableManager closedTradableManager) {
this.tradeManager = tradeManager; this.closedTradableManager = closedTradableManager;
this.user = user;
tradesListChangeListener = change -> applyList(); tradesListChangeListener = change -> applyList();
} }
@ -51,12 +48,12 @@ class ClosedTradesDataModel implements Activatable, DataModel {
@Override @Override
public void activate() { public void activate() {
applyList(); applyList();
tradeManager.getClosedTrades().addListener(tradesListChangeListener); closedTradableManager.getClosedTrades().addListener(tradesListChangeListener);
} }
@Override @Override
public void deactivate() { public void deactivate() {
tradeManager.getClosedTrades().removeListener(tradesListChangeListener); closedTradableManager.getClosedTrades().removeListener(tradesListChangeListener);
} }
public ObservableList<ClosedTradesListItem> getList() { public ObservableList<ClosedTradesListItem> getList() {
@ -64,16 +61,16 @@ class ClosedTradesDataModel implements Activatable, DataModel {
} }
public Offer.Direction getDirection(Offer offer) { public Offer.Direction getDirection(Offer offer) {
return tradeManager.isMyOffer(offer) ? offer.getDirection() : offer.getMirroredDirection(); return closedTradableManager.wasMyOffer(offer) ? offer.getDirection() : offer.getMirroredDirection();
} }
private void applyList() { private void applyList() {
list.clear(); list.clear();
list.addAll(tradeManager.getClosedTrades().stream().map(ClosedTradesListItem::new).collect(Collectors.toList())); list.addAll(closedTradableManager.getClosedTrades().stream().map(ClosedTradesListItem::new).collect(Collectors.toList()));
// we sort by date, earliest first // we sort by date, earliest first
list.sort((o1, o2) -> o2.getTrade().getDate().compareTo(o1.getTrade().getDate())); list.sort((o1, o2) -> o2.getTradable().getDate().compareTo(o1.getTradable().getDate()));
} }
} }

View file

@ -17,22 +17,22 @@
package io.bitsquare.gui.main.portfolio.closedtrades; package io.bitsquare.gui.main.portfolio.closedtrades;
import io.bitsquare.trade.Trade; import io.bitsquare.trade.Tradable;
/** /**
* We could remove that wrapper if it is not needed for additional UI only fields. * We could remove that wrapper if it is not needed for additional UI only fields.
*/ */
class ClosedTradesListItem { class ClosedTradesListItem {
private final Trade trade; private final Tradable tradable;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Constructor // Constructor
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
ClosedTradesListItem(Trade trade) { ClosedTradesListItem(Tradable tradable) {
this.trade = trade; this.tradable = tradable;
} }
@ -40,7 +40,7 @@ class ClosedTradesListItem {
// Getters // Getters
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
Trade getTrade() { Tradable getTradable() {
return trade; return tradable;
} }
} }

View file

@ -20,15 +20,18 @@ package io.bitsquare.gui.main.portfolio.closedtrades;
import io.bitsquare.gui.common.model.ActivatableWithDataModel; import io.bitsquare.gui.common.model.ActivatableWithDataModel;
import io.bitsquare.gui.common.model.ViewModel; import io.bitsquare.gui.common.model.ViewModel;
import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.trade.states.OffererTradeState; import io.bitsquare.trade.Trade;
import io.bitsquare.trade.states.TakerTradeState; import io.bitsquare.trade.offer.OpenOffer;
import io.bitsquare.trade.states.TradeState;
import com.google.inject.Inject; import com.google.inject.Inject;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class ClosedTradesViewModel extends ActivatableWithDataModel<ClosedTradesDataModel> implements ViewModel { class ClosedTradesViewModel extends ActivatableWithDataModel<ClosedTradesDataModel> implements ViewModel {
private static final Logger log = LoggerFactory.getLogger(ClosedTradesViewModel.class);
private final BSFormatter formatter; private final BSFormatter formatter;
@ -45,34 +48,39 @@ class ClosedTradesViewModel extends ActivatableWithDataModel<ClosedTradesDataMod
} }
String getTradeId(ClosedTradesListItem item) { String getTradeId(ClosedTradesListItem item) {
return item.getTrade().getId(); return item.getTradable().getId();
} }
String getAmount(ClosedTradesListItem item) { String getAmount(ClosedTradesListItem item) {
return (item != null) ? formatter.formatCoinWithCode(item.getTrade().getTradeAmount()) : ""; if (item != null && item.getTradable() instanceof Trade)
return formatter.formatCoinWithCode(((Trade) item.getTradable()).getTradeAmount());
else
return "";
} }
String getPrice(ClosedTradesListItem item) { String getPrice(ClosedTradesListItem item) {
return (item != null) ? formatter.formatFiat(item.getTrade().getOffer().getPrice()) : ""; return (item != null) ? formatter.formatFiat(item.getTradable().getOffer().getPrice()) : "";
} }
String getVolume(ClosedTradesListItem item) { String getVolume(ClosedTradesListItem item) {
return (item != null) ? formatter.formatFiatWithCode(item.getTrade().getTradeVolume()) : ""; if (item != null && item.getTradable() instanceof Trade)
return formatter.formatFiatWithCode(((Trade) item.getTradable()).getTradeVolume());
else
return "";
} }
String getDirectionLabel(ClosedTradesListItem item) { String getDirectionLabel(ClosedTradesListItem item) {
return (item != null) ? formatter.formatDirection(dataModel.getDirection(item.getTrade().getOffer())) : ""; return (item != null) ? formatter.formatDirection(dataModel.getDirection(item.getTradable().getOffer())) : "";
} }
String getDate(ClosedTradesListItem item) { String getDate(ClosedTradesListItem item) {
return formatter.formatDateTime(item.getTrade().getDate()); return formatter.formatDateTime(item.getTradable().getDate());
} }
String getState(ClosedTradesListItem item) { String getState(ClosedTradesListItem item) {
if (item != null && item.getTrade() != null) { if (item != null) {
TradeState.LifeCycleState lifeCycleState = item.getTrade().lifeCycleStateProperty().get(); if (item.getTradable() instanceof Trade) {
if (lifeCycleState instanceof TakerTradeState.LifeCycleState) { switch (((Trade) item.getTradable()).lifeCycleStateProperty().get()) {
switch ((TakerTradeState.LifeCycleState) lifeCycleState) {
case COMPLETED: case COMPLETED:
return "Completed"; return "Completed";
case FAILED: case FAILED:
@ -81,17 +89,22 @@ class ClosedTradesViewModel extends ActivatableWithDataModel<ClosedTradesDataMod
throw new RuntimeException("That must not happen. We got a pending state but we are in the closed trades list."); throw new RuntimeException("That must not happen. We got a pending state but we are in the closed trades list.");
} }
} }
else if (lifeCycleState instanceof OffererTradeState.LifeCycleState) { else if (item.getTradable() instanceof OpenOffer) {
switch ((OffererTradeState.LifeCycleState) lifeCycleState) { OpenOffer.State state = ((OpenOffer) item.getTradable()).getState();
case OFFER_CANCELED: log.trace("OpenOffer state {}", state);
switch (state) {
case AVAILABLE:
case RESERVED:
case CLOSED:
log.error("Invalid state {}", state);
return state.toString();
case CANCELED:
return "Canceled"; return "Canceled";
case COMPLETED: /* case FAILED:
return "Completed"; return "Failed";*/
case FAILED: default:
return "Failed"; log.error("Unhandled state {}", state);
case OFFER_OPEN: return state.toString();
case PENDING:
throw new RuntimeException("That must not happen. We got a pending state but we are in the closed trades list.");
} }
} }
} }

View file

@ -17,21 +17,24 @@
package io.bitsquare.gui.main.portfolio.openoffer; package io.bitsquare.gui.main.portfolio.openoffer;
import io.bitsquare.offer.Offer; import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.Trade; import io.bitsquare.trade.offer.OpenOffer;
/** /**
* We could remove that wrapper if it is not needed for additional UI only fields. * We could remove that wrapper if it is not needed for additional UI only fields.
*/ */
class OpenOfferListItem { class OpenOfferListItem {
private final Offer offer; private final OpenOffer openOffer;
public OpenOfferListItem(Trade trade) { public OpenOfferListItem(OpenOffer openOffer) {
this.offer = trade.getOffer(); this.openOffer = openOffer;
} }
public OpenOffer getOpenOffer() {
return openOffer;
}
public Offer getOffer() { public Offer getOffer() {
return offer; return openOffer.getOffer();
} }
} }

View file

@ -21,10 +21,9 @@ import io.bitsquare.common.handlers.ErrorMessageHandler;
import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.gui.common.model.Activatable; import io.bitsquare.gui.common.model.Activatable;
import io.bitsquare.gui.common.model.DataModel; import io.bitsquare.gui.common.model.DataModel;
import io.bitsquare.offer.Offer; import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.Trade; import io.bitsquare.trade.offer.OpenOffer;
import io.bitsquare.trade.TradeManager; import io.bitsquare.trade.offer.OpenOfferManager;
import io.bitsquare.user.User;
import com.google.inject.Inject; import com.google.inject.Inject;
@ -40,16 +39,14 @@ import org.slf4j.LoggerFactory;
class OpenOffersDataModel implements Activatable, DataModel { class OpenOffersDataModel implements Activatable, DataModel {
private static final Logger log = LoggerFactory.getLogger(OpenOffersDataModel.class); private static final Logger log = LoggerFactory.getLogger(OpenOffersDataModel.class);
private final TradeManager tradeManager; private final OpenOfferManager openOfferManager;
private final User user;
private final ObservableList<OpenOfferListItem> list = FXCollections.observableArrayList(); private final ObservableList<OpenOfferListItem> list = FXCollections.observableArrayList();
private final ListChangeListener<Trade> tradesListChangeListener; private final ListChangeListener<OpenOffer> tradesListChangeListener;
@Inject @Inject
public OpenOffersDataModel(TradeManager tradeManager, User user) { public OpenOffersDataModel(OpenOfferManager openOfferManager) {
this.tradeManager = tradeManager; this.openOfferManager = openOfferManager;
this.user = user;
tradesListChangeListener = change -> applyList(); tradesListChangeListener = change -> applyList();
} }
@ -57,16 +54,16 @@ class OpenOffersDataModel implements Activatable, DataModel {
@Override @Override
public void activate() { public void activate() {
applyList(); applyList();
tradeManager.getOpenOfferTrades().addListener(tradesListChangeListener); openOfferManager.getOpenOffers().addListener(tradesListChangeListener);
} }
@Override @Override
public void deactivate() { public void deactivate() {
tradeManager.getOpenOfferTrades().removeListener(tradesListChangeListener); openOfferManager.getOpenOffers().removeListener(tradesListChangeListener);
} }
void onCancelOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { void onCancelOpenOffer(OpenOffer openOffer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
tradeManager.onCancelOpenOffer(offer, resultHandler, errorMessageHandler); openOfferManager.onCancelOpenOffer(openOffer, resultHandler, errorMessageHandler);
} }
@ -75,13 +72,13 @@ class OpenOffersDataModel implements Activatable, DataModel {
} }
public Offer.Direction getDirection(Offer offer) { public Offer.Direction getDirection(Offer offer) {
return tradeManager.isMyOffer(offer) ? offer.getDirection() : offer.getMirroredDirection(); return openOfferManager.isMyOffer(offer) ? offer.getDirection() : offer.getMirroredDirection();
} }
private void applyList() { private void applyList() {
list.clear(); list.clear();
list.addAll(tradeManager.getOpenOfferTrades().stream().map(OpenOfferListItem::new).collect(Collectors.toList())); list.addAll(openOfferManager.getOpenOffers().stream().map(OpenOfferListItem::new).collect(Collectors.toList()));
// we sort by date, earliest first // we sort by date, earliest first
list.sort((o1, o2) -> o2.getOffer().getCreationDate().compareTo(o1.getOffer().getCreationDate())); list.sort((o1, o2) -> o2.getOffer().getCreationDate().compareTo(o1.getOffer().getCreationDate()));

View file

@ -25,7 +25,7 @@ import io.bitsquare.gui.main.MainView;
import io.bitsquare.gui.main.funds.FundsView; import io.bitsquare.gui.main.funds.FundsView;
import io.bitsquare.gui.main.funds.withdrawal.WithdrawalView; import io.bitsquare.gui.main.funds.withdrawal.WithdrawalView;
import io.bitsquare.gui.util.GUIUtil; import io.bitsquare.gui.util.GUIUtil;
import io.bitsquare.offer.Offer; import io.bitsquare.trade.offer.OpenOffer;
import javax.inject.Inject; import javax.inject.Inject;
@ -69,8 +69,8 @@ public class OpenOffersView extends ActivatableViewAndModel<GridPane, OpenOffers
table.setItems(model.getList()); table.setItems(model.getList());
} }
private void onCancelOpenOffer(Offer offer) { private void onCancelOpenOffer(OpenOffer openOffer) {
model.onCancelOpenOffer(offer, model.onCancelOpenOffer(openOffer,
() -> { () -> {
log.debug("Remove offer was successful"); log.debug("Remove offer was successful");
Popups.openInfoPopup("You can withdraw the funds you paid in from the funds screens."); Popups.openInfoPopup("You can withdraw the funds you paid in from the funds screens.");
@ -249,7 +249,7 @@ public class OpenOffersView extends ActivatableViewAndModel<GridPane, OpenOffers
super.updateItem(item, empty); super.updateItem(item, empty);
if (item != null) { if (item != null) {
button.setOnAction(event -> onCancelOpenOffer(item.getOffer())); button.setOnAction(event -> onCancelOpenOffer(item.getOpenOffer()));
setGraphic(button); setGraphic(button);
} }
else { else {

View file

@ -22,7 +22,7 @@ import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.gui.common.model.ActivatableWithDataModel; import io.bitsquare.gui.common.model.ActivatableWithDataModel;
import io.bitsquare.gui.common.model.ViewModel; import io.bitsquare.gui.common.model.ViewModel;
import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.offer.Offer; import io.bitsquare.trade.offer.OpenOffer;
import com.google.inject.Inject; import com.google.inject.Inject;
@ -45,8 +45,8 @@ class OpenOffersViewModel extends ActivatableWithDataModel<OpenOffersDataModel>
} }
void onCancelOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { void onCancelOpenOffer(OpenOffer openOffer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
dataModel.onCancelOpenOffer(offer, resultHandler, errorMessageHandler); dataModel.onCancelOpenOffer(openOffer, resultHandler, errorMessageHandler);
} }
public ObservableList<OpenOfferListItem> getList() { public ObservableList<OpenOfferListItem> getList() {

View file

@ -27,12 +27,12 @@ import io.bitsquare.gui.components.Popups;
import io.bitsquare.gui.main.MainView; import io.bitsquare.gui.main.MainView;
import io.bitsquare.gui.main.portfolio.PortfolioView; import io.bitsquare.gui.main.portfolio.PortfolioView;
import io.bitsquare.gui.main.portfolio.closedtrades.ClosedTradesView; import io.bitsquare.gui.main.portfolio.closedtrades.ClosedTradesView;
import io.bitsquare.offer.Offer;
import io.bitsquare.trade.BuyerTrade; import io.bitsquare.trade.BuyerTrade;
import io.bitsquare.trade.Contract; import io.bitsquare.trade.Contract;
import io.bitsquare.trade.SellerTrade; import io.bitsquare.trade.SellerTrade;
import io.bitsquare.trade.Trade; import io.bitsquare.trade.Trade;
import io.bitsquare.trade.TradeManager; import io.bitsquare.trade.TradeManager;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.states.TradeState; import io.bitsquare.trade.states.TradeState;
import io.bitsquare.user.User; import io.bitsquare.user.User;

View file

@ -23,7 +23,7 @@ import io.bitsquare.locale.BSResources;
import io.bitsquare.locale.Country; import io.bitsquare.locale.Country;
import io.bitsquare.locale.CurrencyUtil; import io.bitsquare.locale.CurrencyUtil;
import io.bitsquare.locale.LanguageUtil; import io.bitsquare.locale.LanguageUtil;
import io.bitsquare.offer.Offer; import io.bitsquare.trade.offer.Offer;
import io.bitsquare.user.User; import io.bitsquare.user.User;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;