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 enum Type {
IRC("", "", 0),
IRC("", "", 2),
SEPA("IBAN", "BIC", WEEK_IN_BLOCKS),
WIRE("primary ID", "secondary ID", WEEK_IN_BLOCKS),
INTERNATIONAL("primary ID", "secondary ID", 2 * WEEK_IN_BLOCKS),

View File

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

View File

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

View File

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

View File

@ -17,9 +17,9 @@
package io.bitsquare.trade;
import io.bitsquare.offer.Offer;
import io.bitsquare.p2p.Peer;
import io.bitsquare.storage.Storage;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.protocol.trade.BuyerAsTakerProtocol;
import io.bitsquare.trade.protocol.trade.BuyerProtocol;
@ -43,7 +43,7 @@ public class BuyerAsTakerTrade extends TakerTrade implements BuyerTrade, Seriali
// 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);
log.trace("Created by constructor");
}

View File

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

View File

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

View File

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

View File

@ -17,9 +17,9 @@
package io.bitsquare.trade;
import io.bitsquare.offer.Offer;
import io.bitsquare.p2p.Peer;
import io.bitsquare.storage.Storage;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.protocol.trade.SellerAsTakerProtocol;
import io.bitsquare.trade.protocol.trade.SellerProtocol;
import io.bitsquare.trade.states.TakerTradeState;
@ -43,7 +43,7 @@ public class SellerAsTakerTrade extends TakerTrade implements SellerTrade, Seria
// 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);
log.trace("Created by constructor");
}

View File

@ -17,9 +17,9 @@
package io.bitsquare.trade;
import io.bitsquare.offer.Offer;
import io.bitsquare.p2p.Peer;
import io.bitsquare.storage.Storage;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.protocol.trade.TakerProtocol;
import io.bitsquare.trade.states.TakerTradeState;
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);
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);
log.trace("Created by constructor");
}
@ -45,7 +45,7 @@ public abstract class TakerTrade extends Trade implements Serializable {
@Override
protected void initStates() {
processState = TakerTradeState.ProcessState.UNDEFINED;
lifeCycleState = TakerTradeState.LifeCycleState.PENDING;
lifeCycleState = Trade.LifeCycleState.PENDING;
initStateProperties();
}
@ -66,16 +66,16 @@ public abstract class TakerTrade extends Trade implements Serializable {
switch ((TakerTradeState.ProcessState) processState) {
case EXCEPTION:
disposeProtocol();
setLifeCycleState(TakerTradeState.LifeCycleState.FAILED);
setLifeCycleState(Trade.LifeCycleState.FAILED);
break;
}
}
@Override
public void setLifeCycleState(TradeState.LifeCycleState lifeCycleState) {
public void setLifeCycleState(Trade.LifeCycleState lifeCycleState) {
super.setLifeCycleState(lifeCycleState);
switch ((TakerTradeState.LifeCycleState) lifeCycleState) {
switch (lifeCycleState) {
case FAILED:
disposeProtocol();
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.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.
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
transient private ObservableList<T> observableList;
@ -47,11 +47,12 @@ public class TradeList<T> extends ArrayList<T> implements Serializable {
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public TradeList(Storage<TradeList> storage, String fileName) {
public TradableList(Storage<TradableList<T>> storage, String fileName) {
log.trace("Created by constructor");
this.storage = storage;
TradeList persisted = storage.initAndGetPersisted(this, fileName);
TradableList persisted = storage.initAndGetPersisted(this, fileName);
if (persisted != null) {
this.addAll(persisted);
}
@ -64,17 +65,17 @@ public class TradeList<T> extends ArrayList<T> implements Serializable {
}
@Override
public boolean add(T trade) {
boolean result = super.add(trade);
getObservableList().add(trade);
public boolean add(T tradable) {
boolean result = super.add(tradable);
getObservableList().add(tradable);
storage.queueUpForSave();
return result;
}
@Override
public boolean remove(Object trade) {
boolean result = super.remove(trade);
getObservableList().remove(trade);
public boolean remove(Object tradable) {
boolean result = super.remove(tradable);
getObservableList().remove(tradable);
storage.queueUpForSave();
return result;
}

View File

@ -25,11 +25,11 @@ import io.bitsquare.common.taskrunner.Model;
import io.bitsquare.crypto.CryptoService;
import io.bitsquare.crypto.KeyRing;
import io.bitsquare.crypto.MessageWithPubKey;
import io.bitsquare.offer.Offer;
import io.bitsquare.p2p.AddressService;
import io.bitsquare.p2p.MessageService;
import io.bitsquare.p2p.Peer;
import io.bitsquare.storage.Storage;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.protocol.trade.ProcessModel;
import io.bitsquare.trade.protocol.trade.TradeProtocol;
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
* 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.
private static final long serialVersionUID = 1L;
private transient static final Logger log = LoggerFactory.getLogger(Trade.class);
public enum LifeCycleState {
PREPARATION,
PENDING,
COMPLETED,
FAILED
}
// Mutable
private Coin tradeAmount;
private Peer tradingPeer;
@ -85,9 +92,9 @@ abstract public class Trade implements Model, Serializable {
// Transient/Immutable
private transient ObjectProperty<TradeState.ProcessState> processStateProperty;
private transient ObjectProperty<TradeState.LifeCycleState> lifeCycleStateProperty;
private transient ObjectProperty<Trade.LifeCycleState> lifeCycleStateProperty;
// Trades are saved in the TradeList
transient private Storage<? extends TradeList> storage;
transient private Storage<? extends TradableList> storage;
transient protected TradeProtocol tradeProtocol;
// Immutable
@ -99,7 +106,7 @@ abstract public class Trade implements Model, Serializable {
private MessageWithPubKey messageWithPubKey;
private Date takeOfferDate;
protected TradeState.ProcessState processState;
protected TradeState.LifeCycleState lifeCycleState;
protected Trade.LifeCycleState lifeCycleState;
private Transaction depositTx;
private Contract contract;
private String contractAsJson;
@ -117,7 +124,7 @@ abstract public class Trade implements Model, Serializable {
// Constructor, initialization
///////////////////////////////////////////////////////////////////////////////////////////
protected Trade(Offer offer, Storage<? extends TradeList> storage) {
protected Trade(Offer offer, Storage<? extends TradableList> storage) {
log.trace("Created by constructor");
this.offer = offer;
this.storage = storage;
@ -135,7 +142,7 @@ abstract public class Trade implements Model, Serializable {
// taker
protected Trade(Offer offer, Coin tradeAmount, Peer tradingPeer,
Storage<? extends TradeList> storage) {
Storage<? extends TradableList> storage) {
this(offer, storage);
this.tradeAmount = tradeAmount;
@ -227,7 +234,7 @@ abstract public class Trade implements Model, Serializable {
this.messageWithPubKey = messageWithPubKey;
}
public void setStorage(Storage<? extends TradeList> storage) {
public void setStorage(Storage<? extends TradableList> storage) {
this.storage = storage;
}
@ -237,7 +244,7 @@ abstract public class Trade implements Model, Serializable {
storage.queueUpForSave();
}
public void setLifeCycleState(TradeState.LifeCycleState lifeCycleState) {
public void setLifeCycleState(Trade.LifeCycleState lifeCycleState) {
this.lifeCycleState = lifeCycleState;
lifeCycleStateProperty.set(lifeCycleState);
storage.queueUpForSave();
@ -302,7 +309,7 @@ abstract public class Trade implements Model, Serializable {
return processStateProperty;
}
public ReadOnlyObjectProperty<? extends TradeState.LifeCycleState> lifeCycleStateProperty() {
public ReadOnlyObjectProperty<Trade.LifeCycleState> lifeCycleStateProperty() {
return lifeCycleStateProperty;
}

View File

@ -22,39 +22,37 @@ import io.bitsquare.btc.AddressEntry;
import io.bitsquare.btc.BlockChainService;
import io.bitsquare.btc.TradeWalletService;
import io.bitsquare.btc.WalletService;
import io.bitsquare.common.handlers.ErrorMessageHandler;
import io.bitsquare.common.handlers.FaultHandler;
import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.crypto.CryptoService;
import io.bitsquare.crypto.KeyRing;
import io.bitsquare.crypto.MessageWithPubKey;
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.DecryptedMessageHandler;
import io.bitsquare.p2p.MailboxMessage;
import io.bitsquare.p2p.MailboxService;
import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.MessageService;
import io.bitsquare.p2p.Peer;
import io.bitsquare.storage.Storage;
import io.bitsquare.trade.closed.ClosedTradableManager;
import io.bitsquare.trade.handlers.TakeOfferResultHandler;
import io.bitsquare.trade.handlers.TransactionResultHandler;
import io.bitsquare.trade.protocol.availability.CheckOfferAvailabilityModel;
import io.bitsquare.trade.protocol.availability.CheckOfferAvailabilityProtocol;
import io.bitsquare.trade.protocol.placeoffer.PlaceOfferModel;
import io.bitsquare.trade.protocol.placeoffer.PlaceOfferProtocol;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.offer.OpenOffer;
import io.bitsquare.trade.offer.OpenOfferManager;
import io.bitsquare.trade.protocol.availability.OfferAvailabilityModel;
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.states.OffererTradeState;
import io.bitsquare.trade.states.TakerTradeState;
import io.bitsquare.user.AccountSettings;
import io.bitsquare.user.User;
import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.InsufficientMoneyException;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.utils.Fiat;
import com.google.common.util.concurrent.FutureCallback;
@ -77,12 +75,13 @@ import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static io.bitsquare.util.Validator.nonEmptyStringOf;
public class TradeManager {
private static final Logger log = LoggerFactory.getLogger(TradeManager.class);
private final User user;
private KeyRing keyRing;
private final AccountSettings accountSettings;
private final KeyRing keyRing;
private final MessageService messageService;
private final MailboxService mailboxService;
private final AddressService addressService;
@ -90,17 +89,13 @@ public class TradeManager {
private final WalletService walletService;
private final TradeWalletService tradeWalletService;
private final CryptoService<MailboxMessage> cryptoService;
private final OfferBookService offerBookService;
private OpenOfferManager openOfferManager;
private ClosedTradableManager closedTradableManager;
private final ArbitrationRepository arbitrationRepository;
private final Map<String, CheckOfferAvailabilityProtocol> checkOfferAvailabilityProtocolMap = new HashMap<>();
private final Storage<TradeList> pendingTradesStorage;
private final Storage<TradeList> openOfferTradesStorage;
private final TradeList<Trade> openOfferTrades;
private final TradeList<Trade> pendingTrades;
private final TradeList<Trade> closedTrades;
private boolean shutDownRequested;
private final Map<String, OfferAvailabilityProtocol> checkOfferAvailabilityProtocolMap = new HashMap<>();
private final Storage<TradableList<Trade>> pendingTradesStorage;
private final TradableList<Trade> pendingTrades;
///////////////////////////////////////////////////////////////////////////////////////////
@ -110,7 +105,6 @@ public class TradeManager {
@Inject
public TradeManager(User user,
KeyRing keyRing,
AccountSettings accountSettings,
MessageService messageService,
MailboxService mailboxService,
AddressService addressService,
@ -118,12 +112,12 @@ public class TradeManager {
WalletService walletService,
TradeWalletService tradeWalletService,
CryptoService<MailboxMessage> cryptoService,
OfferBookService offerBookService,
OpenOfferManager openOfferManager,
ClosedTradableManager closedTradableManager,
ArbitrationRepository arbitrationRepository,
@Named("storage.dir") File storageDir) {
this.user = user;
this.keyRing = keyRing;
this.accountSettings = accountSettings;
this.messageService = messageService;
this.mailboxService = mailboxService;
this.addressService = addressService;
@ -131,20 +125,12 @@ public class TradeManager {
this.walletService = walletService;
this.tradeWalletService = tradeWalletService;
this.cryptoService = cryptoService;
this.offerBookService = offerBookService;
this.openOfferManager = openOfferManager;
this.closedTradableManager = closedTradableManager;
this.arbitrationRepository = arbitrationRepository;
openOfferTradesStorage = new Storage<>(storageDir);
pendingTradesStorage = new Storage<>(storageDir);
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);
this.pendingTrades = new TradableList<>(pendingTradesStorage, "PendingTrades");
}
@ -156,16 +142,6 @@ public class TradeManager {
// OffererAsBuyerProtocol listens for take offer requests, so we need to instantiate it early.
public void 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
// We run that before initializing the pending trades to be sure the state is correct
@ -173,10 +149,69 @@ public class TradeManager {
(encryptedMailboxMessages) -> {
log.trace("mailboxService.getAllMessages success");
setMailboxMessagesToTrades(encryptedMailboxMessages);
//TODO testing
//emptyMailbox();
emptyMailbox();
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) {
@ -208,20 +243,13 @@ public class TradeManager {
}
private void initPendingTrades() {
log.trace("initPendingTrades");
List<Trade> failedTrades = new ArrayList<>();
for (Trade trade : pendingTrades) {
// 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
// continue the trade, but that might fail.
boolean failed = false;
if (trade instanceof TakerTrade)
failed = trade.lifeCycleState == TakerTradeState.LifeCycleState.FAILED;
else if (trade instanceof OffererTrade)
failed = trade.lifeCycleState == OffererTradeState.LifeCycleState.FAILED;
if (failed) {
if (trade.lifeCycleState == Trade.LifeCycleState.FAILED) {
failedTrades.add(trade);
}
else {
@ -232,63 +260,21 @@ public class TradeManager {
}
for (Trade trade : failedTrades) {
pendingTrades.remove(trade);
closedTrades.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);
}
closedTradableManager.add(trade);
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// Offer
// Called from Offerbook when offer gets removed from DHT
///////////////////////////////////////////////////////////////////////////////////////////
public void placeOffer(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 -> handlePlaceOfferResult(transaction, offer, resultHandler),
errorMessageHandler::handleErrorMessage
);
placeOfferProtocol.placeOffer();
public void onOfferRemovedFromRemoteOfferBook(Offer offer) {
disposeCheckOfferAvailabilityRequest(offer);
}
private void handlePlaceOfferResult(Transaction transaction, Offer offer, TransactionResultHandler resultHandler) {
/* private void handlePlaceOfferResult(Transaction transaction, Offer offer, TransactionResultHandler resultHandler) {
Trade trade;
if (offer.getDirection() == Offer.Direction.BUY)
trade = new BuyerAsOffererTrade(offer, openOfferTradesStorage);
@ -299,9 +285,9 @@ public class TradeManager {
initTrade(trade);
setupDepositPublishedListener(trade);
resultHandler.handleResult(transaction);
}
}*/
private void setupDepositPublishedListener(Trade trade) {
/* private void setupDepositPublishedListener(Trade trade) {
trade.processStateProperty().addListener((ov, oldValue, newValue) -> {
log.debug("setupDepositPublishedListener state = " + newValue);
if (newValue == OffererTradeState.ProcessState.DEPOSIT_PUBLISHED) {
@ -314,13 +300,13 @@ public class TradeManager {
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);
}
private void removeOpenOffer(Offer offer,
*/
/* private void removeOpenOffer(Offer offer,
ResultHandler resultHandler,
ErrorMessageHandler errorMessageHandler,
boolean isCancelRequest) {
@ -345,7 +331,7 @@ public class TradeManager {
resultHandler.handleResult();
},
(message, throwable) -> errorMessageHandler.handleErrorMessage(message));
}
}*/
///////////////////////////////////////////////////////////////////////////////////////////
@ -354,13 +340,13 @@ public class TradeManager {
public void checkOfferAvailability(Offer offer) {
if (!checkOfferAvailabilityProtocolMap.containsKey(offer.getId())) {
CheckOfferAvailabilityModel model = new CheckOfferAvailabilityModel(
OfferAvailabilityModel model = new OfferAvailabilityModel(
offer,
keyRing.getPubKeyRing(),
messageService,
addressService);
CheckOfferAvailabilityProtocol protocol = new CheckOfferAvailabilityProtocol(model,
OfferAvailabilityProtocol protocol = new OfferAvailabilityProtocol(model,
() -> disposeCheckOfferAvailabilityRequest(offer),
(errorMessage) -> disposeCheckOfferAvailabilityRequest(offer));
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
public void requestTakeOffer(Coin amount, Offer offer, TakeOfferResultHandler takeOfferResultHandler) {
CheckOfferAvailabilityModel model = new CheckOfferAvailabilityModel(offer, keyRing.getPubKeyRing(), messageService, addressService);
CheckOfferAvailabilityProtocol availabilityProtocol = new CheckOfferAvailabilityProtocol(model,
OfferAvailabilityModel model = new OfferAvailabilityModel(offer, keyRing.getPubKeyRing(), messageService, addressService);
OfferAvailabilityProtocol availabilityProtocol = new OfferAvailabilityProtocol(model,
() -> createTrade(amount, offer, model, takeOfferResultHandler),
(errorMessage) -> disposeCheckOfferAvailabilityRequest(offer));
checkOfferAvailabilityProtocolMap.put(offer.getId(), availabilityProtocol);
availabilityProtocol.checkOfferAvailability();
}
private void createTrade(Coin amount, Offer offer, CheckOfferAvailabilityModel model, TakeOfferResultHandler
private void createTrade(Coin amount, Offer offer, OfferAvailabilityModel model, TakeOfferResultHandler
takeOfferResultHandler) {
disposeCheckOfferAvailabilityRequest(offer);
if (offer.getState() == Offer.State.AVAILABLE) {
@ -419,13 +405,10 @@ public class TradeManager {
public void onSuccess(@javax.annotation.Nullable Transaction transaction) {
if (transaction != null) {
log.info("onWithdraw onSuccess tx ID:" + transaction.getHashAsString());
if (trade instanceof OffererTrade)
trade.setLifeCycleState(OffererTradeState.LifeCycleState.COMPLETED);
else if (trade instanceof TakerTrade)
trade.setLifeCycleState(TakerTradeState.LifeCycleState.COMPLETED);
trade.setLifeCycleState(Trade.LifeCycleState.COMPLETED);
pendingTrades.remove(trade);
closedTrades.add(trade);
closedTradableManager.add(trade);
resultHandler.handleResult();
}
@ -452,27 +435,27 @@ public class TradeManager {
// Called from Offerbook when offer gets removed from DHT
///////////////////////////////////////////////////////////////////////////////////////////
public void onOfferRemovedFromRemoteOfferBook(Offer offer) {
/* public void onOfferRemovedFromRemoteOfferBook(Offer offer) {
disposeCheckOfferAvailabilityRequest(offer);
}
*/
///////////////////////////////////////////////////////////////////////////////////////////
// Getters
///////////////////////////////////////////////////////////////////////////////////////////
public ObservableList<Trade> getOpenOfferTrades() {
/* public ObservableList<Trade> getOpenOfferTrades() {
return openOfferTrades.getObservableList();
}
}*/
public ObservableList<Trade> getPendingTrades() {
return pendingTrades.getObservableList();
}
public ObservableList<Trade> getClosedTrades() {
/* public ObservableList<Trade> getClosedTrades() {
return closedTrades.getObservableList();
}
*/
///////////////////////////////////////////////////////////////////////////////////////////
// Misc
@ -480,7 +463,7 @@ public class TradeManager {
private void disposeCheckOfferAvailabilityRequest(Offer offer) {
if (checkOfferAvailabilityProtocolMap.containsKey(offer.getId())) {
CheckOfferAvailabilityProtocol protocol = checkOfferAvailabilityProtocolMap.get(offer.getId());
OfferAvailabilityProtocol protocol = checkOfferAvailabilityProtocolMap.get(offer.getId());
protocol.cancel();
checkOfferAvailabilityProtocolMap.remove(offer.getId());
}
@ -499,6 +482,6 @@ public class TradeManager {
}
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;
import io.bitsquare.BitsquareModule;
import io.bitsquare.trade.closed.ClosedTradableManager;
import com.google.inject.Injector;
import com.google.inject.Singleton;
import org.slf4j.Logger;
@ -37,11 +37,12 @@ public class TradeModule extends BitsquareModule {
@Override
protected void configure() {
bind(TradeManager.class).in(Singleton.class);
bind(ClosedTradableManager.class).in(Singleton.class);
}
@Override
/* @Override
protected void doClose(Injector injector) {
log.trace("doClose " + getClass().getSimpleName());
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/>.
*/
package io.bitsquare.offer;
package io.bitsquare.trade.offer;
import io.bitsquare.btc.Restrictions;
import io.bitsquare.crypto.KeyRing;
import io.bitsquare.crypto.PubKeyRing;
import io.bitsquare.fiat.FiatAccount;
import io.bitsquare.locale.Country;
@ -51,12 +52,11 @@ public class Offer implements Serializable {
public enum Direction {BUY, SELL}
public enum State {
UNKNOWN,
UNDEFINED,
AVAILABLE,
RESERVED,
NOT_AVAILABLE,
REMOVED,
OFFERER_OFFLINE,
FAULT
OFFERER_OFFLINE
}
@ -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!
private String offerFeePaymentTxID;
private State state = State.UNKNOWN;
private State state = State.UNDEFINED;
// 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
@ -125,7 +125,7 @@ public class Offer implements Serializable {
this.acceptedLanguageCodes = acceptedLanguageCodes;
creationDate = new Date();
setState(State.UNKNOWN);
setState(State.UNDEFINED);
}
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
}
public boolean isMyOffer(KeyRing keyRing) {
return getPubKeyRing().getHashString().equals(keyRing.getPubKeyRing().getHashString());
}
public Fiat getVolumeByAmount(Coin amount) {
if (fiatPrice != 0 && amount != null && !amount.isZero())
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/>.
*/
package io.bitsquare.offer;
package io.bitsquare.trade.offer;
import io.bitsquare.common.handlers.FaultHandler;
import io.bitsquare.common.handlers.ResultHandler;

View File

@ -15,13 +15,20 @@
* 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 com.google.inject.Injector;
import com.google.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
public abstract class OfferModule extends BitsquareModule {
private static final Logger log = LoggerFactory.getLogger(OfferModule.class);
protected OfferModule(Environment env) {
super(env);
@ -29,9 +36,16 @@ public abstract class OfferModule extends BitsquareModule {
@Override
protected final void configure() {
bind(OpenOfferManager.class).in(Singleton.class);
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/>.
*/
package io.bitsquare.offer.tomp2p;
package io.bitsquare.trade.offer.tomp2p;
import io.bitsquare.common.handlers.FaultHandler;
import io.bitsquare.common.handlers.ResultHandler;
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.TomP2PNode;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.offer.OfferBookService;
import java.io.IOException;

View File

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

View File

@ -18,22 +18,29 @@
package io.bitsquare.trade.protocol.availability.messages;
import io.bitsquare.crypto.PubKeyRing;
import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
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.
private static final long serialVersionUID = 1L;
private final PubKeyRing pubKeyRing;
public RequestIsOfferAvailableMessage(String tradeId, PubKeyRing pubKeyRing) {
super(tradeId);
public OfferAvailabilityRequest(String offerId, PubKeyRing pubKeyRing) {
super(offerId);
this.pubKeyRing = pubKeyRing;
}
public PubKeyRing getPubKeyRing() {
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;
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.
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);
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.TaskRunner;
import io.bitsquare.offer.Offer;
import io.bitsquare.p2p.Peer;
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.LoggerFactory;
public class GetPeerAddress extends Task<CheckOfferAvailabilityModel> {
public class GetPeerAddress extends Task<OfferAvailabilityModel> {
private static final Logger log = LoggerFactory.getLogger(GetPeerAddress.class);
public GetPeerAddress(TaskRunner taskHandler, CheckOfferAvailabilityModel model) {
public GetPeerAddress(TaskRunner taskHandler, OfferAvailabilityModel model) {
super(taskHandler, model);
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) {
model.offer.setState(Offer.State.FAULT);
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.TaskRunner;
import io.bitsquare.offer.Offer;
import io.bitsquare.trade.protocol.availability.CheckOfferAvailabilityModel;
import io.bitsquare.trade.protocol.availability.messages.ReportOfferAvailabilityMessage;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.protocol.availability.OfferAvailabilityModel;
import io.bitsquare.trade.protocol.availability.messages.OfferAvailabilityResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ProcessReportOfferAvailabilityMessage extends Task<CheckOfferAvailabilityModel> {
private static final Logger log = LoggerFactory.getLogger(ProcessReportOfferAvailabilityMessage.class);
public class ProcessOfferAvailabilityResponse extends Task<OfferAvailabilityModel> {
private static final Logger log = LoggerFactory.getLogger(ProcessOfferAvailabilityResponse.class);
public ProcessReportOfferAvailabilityMessage(TaskRunner taskHandler, CheckOfferAvailabilityModel model) {
public ProcessOfferAvailabilityResponse(TaskRunner taskHandler, OfferAvailabilityModel model) {
super(taskHandler, model);
}
@Override
protected void doRun() {
try {
ReportOfferAvailabilityMessage reportOfferAvailabilityMessage = (ReportOfferAvailabilityMessage) model.getMessage();
OfferAvailabilityResponse offerAvailabilityResponse = (OfferAvailabilityResponse) model.getMessage();
if (model.offer.getState() != Offer.State.REMOVED) {
if (reportOfferAvailabilityMessage.isOfferOpen)
if (offerAvailabilityResponse.isAvailable)
model.offer.setState(Offer.State.AVAILABLE);
else
model.offer.setState(Offer.State.RESERVED);
model.offer.setState(Offer.State.NOT_AVAILABLE);
}
complete();
} catch (Throwable t) {
model.offer.setState(Offer.State.FAULT);
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.TaskRunner;
import io.bitsquare.offer.Offer;
import io.bitsquare.p2p.listener.SendMessageListener;
import io.bitsquare.trade.protocol.availability.CheckOfferAvailabilityModel;
import io.bitsquare.trade.protocol.availability.messages.RequestIsOfferAvailableMessage;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.protocol.availability.OfferAvailabilityModel;
import io.bitsquare.trade.protocol.availability.messages.OfferAvailabilityRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SendRequestIsOfferAvailableMessage extends Task<CheckOfferAvailabilityModel> {
private static final Logger log = LoggerFactory.getLogger(SendRequestIsOfferAvailableMessage.class);
private static final long serialVersionUID = 1L;
public class SendOfferAvailabilityRequest extends Task<OfferAvailabilityModel> {
private static final Logger log = LoggerFactory.getLogger(SendOfferAvailabilityRequest.class);
public SendRequestIsOfferAvailableMessage(TaskRunner taskHandler, CheckOfferAvailabilityModel model) {
public SendOfferAvailabilityRequest(TaskRunner taskHandler, OfferAvailabilityModel model) {
super(taskHandler, model);
}
@Override
protected void doRun() {
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.offer.getPubKeyRing(),
message,
@ -56,8 +55,6 @@ public class SendRequestIsOfferAvailableMessage extends Task<CheckOfferAvailabil
}
});
} catch (Throwable t) {
model.offer.setState(Offer.State.FAULT);
failed(t);
}
}

View File

@ -20,8 +20,8 @@ package io.bitsquare.trade.protocol.placeoffer;
import io.bitsquare.btc.TradeWalletService;
import io.bitsquare.btc.WalletService;
import io.bitsquare.common.taskrunner.Model;
import io.bitsquare.offer.Offer;
import io.bitsquare.offer.OfferBookService;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.offer.OfferBookService;
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.Peer;
import io.bitsquare.p2p.listener.SendMessageListener;
import io.bitsquare.trade.BuyerAsOffererTrade;
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.RequestPublishDepositTxMessage;
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.shared.CommitPayoutTx;
import io.bitsquare.trade.protocol.trade.tasks.shared.SetupPayoutTxLockTimeReachedListener;
import io.bitsquare.trade.states.OffererTradeState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -72,6 +67,26 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc
// 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
public void doApplyMailboxMessage(Message message, Trade trade) {
this.trade = trade;
@ -93,61 +108,6 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc
// 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) {
stopTimeout();
processModel.setTradeMessage(tradeMessage);
@ -217,13 +177,7 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc
@Override
protected void doHandleDecryptedMessage(TradeMessage tradeMessage, Peer sender) {
if (tradeMessage instanceof RequestIsOfferAvailableMessage) {
handle((RequestIsOfferAvailableMessage) tradeMessage, sender);
}
else if (tradeMessage instanceof RequestDepositTxInputsMessage) {
handle((RequestDepositTxInputsMessage) tradeMessage, sender);
}
else if (tradeMessage instanceof RequestPublishDepositTxMessage) {
if (tradeMessage instanceof RequestPublishDepositTxMessage) {
handle((RequestPublishDepositTxMessage) tradeMessage);
}
else if (tradeMessage instanceof RequestFinalizePayoutTxMessage) {

View File

@ -17,5 +17,9 @@
package io.bitsquare.trade.protocol.trade;
import io.bitsquare.p2p.Peer;
import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
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.PubKeyRing;
import io.bitsquare.fiat.FiatAccount;
import io.bitsquare.offer.Offer;
import io.bitsquare.p2p.AddressService;
import io.bitsquare.p2p.MessageService;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
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.Peer;
import io.bitsquare.p2p.listener.SendMessageListener;
import io.bitsquare.trade.SellerAsOffererTrade;
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.FiatTransferStartedMessage;
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.tasks.offerer.VerifyTakeOfferFeePayment;
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.shared.CommitPayoutTx;
import io.bitsquare.trade.protocol.trade.tasks.shared.SetupPayoutTxLockTimeReachedListener;
import io.bitsquare.trade.states.OffererTradeState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static io.bitsquare.util.Validator.checkTradeId;
public class SellerAsOffererProtocol extends TradeProtocol implements SellerProtocol, OffererProtocol {
private static final Logger log = LoggerFactory.getLogger(SellerAsOffererProtocol.class);
@ -72,6 +65,26 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
// 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
public void doApplyMailboxMessage(Message message, Trade trade) {
this.trade = trade;
@ -101,67 +114,6 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
// 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) {
stopTimeout();
processModel.setTradeMessage(tradeMessage);
@ -239,13 +191,7 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
@Override
protected void doHandleDecryptedMessage(TradeMessage tradeMessage, Peer sender) {
if (tradeMessage instanceof RequestIsOfferAvailableMessage) {
handle((RequestIsOfferAvailableMessage) tradeMessage, sender);
}
else if (tradeMessage instanceof RequestPayDepositMessage) {
handle((RequestPayDepositMessage) tradeMessage, sender);
}
else if (tradeMessage instanceof DepositTxPublishedMessage) {
if (tradeMessage instanceof DepositTxPublishedMessage) {
handle((DepositTxPublishedMessage) tradeMessage);
}
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> buyerOutputs;
public final byte[] buyerTradeWalletPubKey;
public final boolean isInitialRequest;
public final PubKeyRing buyerPubKeyRing;
public final FiatAccount buyerFiatAccount;
public final String buyerAccountId;
@ -44,6 +45,7 @@ public class RequestPayDepositMessage extends TradeMessage implements Serializab
public RequestPayDepositMessage(String tradeId,
Coin tradeAmount,
boolean isInitialRequest,
List<TransactionOutput> buyerConnectedOutputsForAllInputs,
List<TransactionOutput> buyerOutputs,
byte[] buyerTradeWalletPubKey,
@ -52,6 +54,7 @@ public class RequestPayDepositMessage extends TradeMessage implements Serializab
String buyerAccountId) {
super(tradeId);
this.tradeAmount = tradeAmount;
this.isInitialRequest = isInitialRequest;
this.buyerPubKeyRing = buyerPubKeyRing;
this.buyerConnectedOutputsForAllInputs = buyerConnectedOutputsForAllInputs;
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.p2p.listener.SendMessageListener;
import io.bitsquare.trade.BuyerAsTakerTrade;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.protocol.trade.TradeTask;
import io.bitsquare.trade.protocol.trade.messages.RequestPayDepositMessage;
@ -37,9 +38,11 @@ public class SendRequestPayDepositMessage extends TradeTask {
@Override
protected void doRun() {
try {
boolean isInitialRequest = trade instanceof BuyerAsTakerTrade;
RequestPayDepositMessage tradeMessage = new RequestPayDepositMessage(
processModel.getId(),
trade.getTradeAmount(),
isInitialRequest,
processModel.getConnectedOutputsForAllInputs(),
processModel.getOutputs(),
processModel.getTradeWalletPubKey(),

View File

@ -66,14 +66,11 @@ public class SignAndPublishDepositTx extends TradeTask {
trade.setDepositTx(transaction);
if (trade instanceof TakerTrade) {
trade.setLifeCycleState(Trade.LifeCycleState.PENDING);
if (trade instanceof TakerTrade)
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.setLifeCycleState(OffererTradeState.LifeCycleState.PENDING);
}
trade.setTakeOfferDate(new Date());
@ -95,7 +92,7 @@ public class SignAndPublishDepositTx extends TradeTask {
trade.setThrowable(t);
if (trade instanceof OffererTrade)
trade.setLifeCycleState(OffererTradeState.LifeCycleState.OFFER_OPEN);
trade.setLifeCycleState(Trade.LifeCycleState.PREPARATION);
failed(t);
}

View File

@ -23,19 +23,10 @@ import org.slf4j.LoggerFactory;
public class OffererTradeState {
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 {
UNDEFINED,
DEPOSIT_PUBLISHED,
DEPOSIT_CONFIRMED,
DEPOSIT_CONFIRMED,
FIAT_PAYMENT_STARTED,
@ -43,7 +34,7 @@ public class OffererTradeState {
PAYOUT_FINALIZED,
PAYOUT_BROAD_CASTED,
PAYOUT_BROAD_CASTED_FAILED,
PAYOUT_BROAD_CASTED_FAILED,
MESSAGE_SENDING_FAILED,
TIMEOUT,

View File

@ -36,6 +36,6 @@ public class StateUtil {
public static void setOfferOpenState(Trade trade) {
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 {
private static final Logger log = LoggerFactory.getLogger(TakerTradeState.class);
public enum LifeCycleState implements TradeState.LifeCycleState {
PENDING,
COMPLETED,
FAILED
}
public enum ProcessState implements TradeState.ProcessState {
UNDEFINED,
TAKE_OFFER_FEE_TX_CREATED,

View File

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

View File

@ -18,10 +18,10 @@
package io.bitsquare.trade.protocol.placeoffer;
import io.bitsquare.btc.WalletService;
import io.bitsquare.offer.OfferBookService;
import io.bitsquare.p2p.MessageService;
import io.bitsquare.p2p.tomp2p.BootstrappedPeerBuilder;
import io.bitsquare.p2p.tomp2p.TomP2PNode;
import io.bitsquare.trade.offer.OfferBookService;
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.KeyStorage;
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.tomp2p.TomP2PModule;
import io.bitsquare.storage.Storage;
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.Preferences;
import io.bitsquare.user.User;

View File

@ -31,6 +31,7 @@ import io.bitsquare.p2p.BootstrapState;
import io.bitsquare.p2p.ClientNode;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.TradeManager;
import io.bitsquare.trade.offer.OpenOfferManager;
import io.bitsquare.user.User;
import com.google.inject.Inject;
@ -91,12 +92,13 @@ class MainViewModel implements ViewModel {
private final ArbitrationRepository arbitrationRepository;
private final ClientNode clientNode;
private final TradeManager tradeManager;
private OpenOfferManager openOfferManager;
private final UpdateProcess updateProcess;
private final BSFormatter formatter;
@Inject
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) {
this.user = user;
this.keyRing = keyRing;
@ -104,6 +106,7 @@ class MainViewModel implements ViewModel {
this.arbitrationRepository = arbitrationRepository;
this.clientNode = clientNode;
this.tradeManager = tradeManager;
this.openOfferManager = openOfferManager;
this.updateProcess = updateProcess;
this.formatter = formatter;
@ -211,6 +214,7 @@ class MainViewModel implements ViewModel {
log.debug("loadAllArbitrators");
arbitrationRepository.loadAllArbitrators();
tradeManager.onAllServicesInitialized();
openOfferManager.onAllServicesInitialized();
}
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.gui.common.view.FxmlView;
import io.bitsquare.gui.common.view.InitializableView;
import io.bitsquare.trade.protocol.availability.CheckOfferAvailabilityProtocol;
import io.bitsquare.trade.protocol.availability.tasks.ProcessReportOfferAvailabilityMessage;
import io.bitsquare.trade.protocol.availability.tasks.SendRequestIsOfferAvailableMessage;
import io.bitsquare.trade.protocol.availability.OfferAvailabilityProtocol;
import io.bitsquare.trade.protocol.availability.tasks.ProcessOfferAvailabilityResponse;
import io.bitsquare.trade.protocol.availability.tasks.SendOfferAvailabilityRequest;
import io.bitsquare.trade.protocol.placeoffer.PlaceOfferProtocol;
import io.bitsquare.trade.protocol.placeoffer.tasks.AddOfferToRemoteOfferBook;
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(
/*---- Protocol ----*/
CheckOfferAvailabilityProtocol.class,
OfferAvailabilityProtocol.class,
io.bitsquare.trade.protocol.availability.tasks.GetPeerAddress.class,
SendRequestIsOfferAvailableMessage.class,
ProcessReportOfferAvailabilityMessage.class,
SendOfferAvailabilityRequest.class,
ProcessOfferAvailabilityResponse.class,
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.GUIUtil;
import io.bitsquare.trade.TradeManager;
import io.bitsquare.trade.offer.OpenOfferManager;
import org.bitcoinj.core.Coin;
@ -51,13 +52,15 @@ public class ReservedView extends ActivatableViewAndModel {
private final WalletService walletService;
private final TradeManager tradeManager;
private OpenOfferManager openOfferManager;
private final BSFormatter formatter;
private final ObservableList<ReservedListItem> addressList = FXCollections.observableArrayList();
@Inject
private ReservedView(WalletService walletService, TradeManager tradeManager, BSFormatter formatter) {
private ReservedView(WalletService walletService, TradeManager tradeManager, OpenOfferManager openOfferManager, BSFormatter formatter) {
this.walletService = walletService;
this.tradeManager = tradeManager;
this.openOfferManager = openOfferManager;
this.formatter = formatter;
}
@ -94,8 +97,8 @@ public class ReservedView extends ActivatableViewAndModel {
private void fillList() {
addressList.clear();
addressList.addAll(Stream.concat(tradeManager.getOpenOfferTrades().stream(), tradeManager.getPendingTrades().stream())
.map(trade -> new ReservedListItem(walletService.getAddressEntry(trade.getId()), walletService, formatter))
addressList.addAll(Stream.concat(openOfferManager.getOpenOffers().stream(), tradeManager.getPendingTrades().stream())
.map(tradable -> new ReservedListItem(walletService.getAddressEntry(tradable.getOffer().getId()), walletService, formatter))
.collect(Collectors.toList()));
// 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.GUIUtil;
import io.bitsquare.trade.TradeManager;
import io.bitsquare.trade.offer.OpenOfferManager;
import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Coin;
@ -67,13 +68,15 @@ public class WithdrawalView extends ActivatableViewAndModel {
private final WalletService walletService;
private TradeManager tradeManager;
private OpenOfferManager openOfferManager;
private final BSFormatter formatter;
private final ObservableList<WithdrawalListItem> addressList = FXCollections.observableArrayList();
@Inject
private WithdrawalView(WalletService walletService, TradeManager tradeManager, BSFormatter formatter) {
private WithdrawalView(WalletService walletService, TradeManager tradeManager, OpenOfferManager openOfferManager, BSFormatter formatter) {
this.walletService = walletService;
this.tradeManager = tradeManager;
this.openOfferManager = openOfferManager;
this.formatter = formatter;
}
@ -181,8 +184,8 @@ public class WithdrawalView extends ActivatableViewAndModel {
addressList.clear();
List<AddressEntry> addressEntryList = walletService.getAddressEntryList();
List<String> reservedTrades = Stream.concat(tradeManager.getOpenOfferTrades().stream(), tradeManager.getPendingTrades().stream())
.map(trade -> trade.getId())
List<String> reservedTrades = Stream.concat(openOfferManager.getOpenOffers().stream(), tradeManager.getPendingTrades().stream())
.map(tradable -> tradable.getOffer().getId())
.collect(Collectors.toList());
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.offerbook.OfferBookView;
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.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.util.BSFormatter;
import io.bitsquare.locale.Country;
import io.bitsquare.offer.Offer;
import io.bitsquare.trade.TradeManager;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.offer.OpenOfferManager;
import io.bitsquare.user.AccountSettings;
import io.bitsquare.user.Preferences;
import io.bitsquare.user.User;
@ -67,7 +67,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
class CreateOfferDataModel implements Activatable, DataModel {
private static final Logger log = LoggerFactory.getLogger(CreateOfferDataModel.class);
private final TradeManager tradeManager;
private OpenOfferManager openOfferManager;
private final WalletService walletService;
private final AccountSettings accountSettings;
private final Preferences preferences;
@ -106,11 +106,10 @@ class CreateOfferDataModel implements Activatable, DataModel {
// non private for testing
@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) {
this.tradeManager = tradeManager;
this.openOfferManager = openOfferManager;
this.walletService = walletService;
ArbitratorService arbitratorService1 = arbitratorService;
this.accountSettings = accountSettings;
this.preferences = preferences;
this.formatter = formatter;
@ -163,9 +162,9 @@ class CreateOfferDataModel implements Activatable, DataModel {
// no-op
}
void placeOffer() {
void onPlaceOffer() {
// data validation is done in the trade domain
tradeManager.placeOffer(offerId,
openOfferManager.onPlaceOffer(offerId,
direction,
priceAsFiat.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.util.ImageUtil;
import io.bitsquare.locale.BSResources;
import io.bitsquare.offer.Offer;
import io.bitsquare.trade.offer.Offer;
import org.bitcoinj.core.Coin;
import org.bitcoinj.utils.Fiat;
@ -141,7 +141,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
@FXML
void onPlaceOffer() {
model.placeOffer();
model.onPlaceOffer();
}
@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.InputValidator;
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.Coin;
@ -154,13 +154,13 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
}
void placeOffer() {
void onPlaceOffer() {
dataModel.requestPlaceOfferErrorMessage.set(null);
dataModel.requestPlaceOfferSuccess.set(false);
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.locale.Country;
import io.bitsquare.locale.CurrencyUtil;
import io.bitsquare.offer.Offer;
import io.bitsquare.offer.OfferBookService;
import io.bitsquare.trade.TradeManager;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.offer.OfferBookService;
import io.bitsquare.user.User;
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
offer.setState(Offer.State.REMOVED);
// clean up possible references in tradeManager
// clean up possible references in openOfferManager
tradeManager.onOfferRemovedFromRemoteOfferBook(offer);
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.locale.Country;
import io.bitsquare.locale.CurrencyUtil;
import io.bitsquare.offer.Offer;
import io.bitsquare.trade.TradeManager;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.offer.OpenOfferManager;
import io.bitsquare.user.Preferences;
import io.bitsquare.user.User;
@ -58,7 +58,7 @@ class OfferBookDataModel implements Activatable, DataModel {
private final OfferBook offerBook;
private final Preferences preferences;
private final BSFormatter formatter;
private final TradeManager tradeManager;
private final OpenOfferManager openOfferManager;
private final FilteredList<OfferBookListItem> filteredItems;
private final SortedList<OfferBookListItem> sortedItems;
@ -77,9 +77,9 @@ class OfferBookDataModel implements Activatable, DataModel {
@Inject
public OfferBookDataModel(User user, TradeManager tradeManager, OfferBook offerBook, Preferences preferences,
public OfferBookDataModel(User user, OpenOfferManager openOfferManager, OfferBook offerBook, Preferences preferences,
BSFormatter formatter) {
this.tradeManager = tradeManager;
this.openOfferManager = openOfferManager;
this.user = user;
this.offerBook = offerBook;
this.preferences = preferences;
@ -112,7 +112,7 @@ class OfferBookDataModel implements Activatable, DataModel {
}
void onCancelOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
tradeManager.onCancelOpenOffer(offer, resultHandler, errorMessageHandler);
openOfferManager.onCancelOpenOffer(offer, resultHandler, errorMessageHandler);
}
void calculateVolume() {
@ -210,7 +210,7 @@ class OfferBookDataModel implements Activatable, DataModel {
}
boolean isMyOffer(Offer offer) {
return tradeManager.isMyOffer(offer);
return openOfferManager.isMyOffer(offer);
}
Coin getAmountAsCoin() {

View File

@ -18,7 +18,7 @@
package io.bitsquare.gui.main.offer.offerbook;
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.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.OptionalFiatValidator;
import io.bitsquare.locale.BSResources;
import io.bitsquare.offer.Offer;
import io.bitsquare.trade.offer.Offer;
import java.util.ArrayList;
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.OptionalFiatValidator;
import io.bitsquare.locale.BSResources;
import io.bitsquare.offer.Offer;
import io.bitsquare.trade.offer.Offer;
import org.bitcoinj.core.Coin;
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.gui.common.model.Activatable;
import io.bitsquare.gui.common.model.DataModel;
import io.bitsquare.offer.Offer;
import io.bitsquare.trade.TradeManager;
import io.bitsquare.trade.handlers.TakeOfferResultHandler;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.user.Preferences;
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.util.ImageUtil;
import io.bitsquare.locale.BSResources;
import io.bitsquare.offer.Offer;
import io.bitsquare.trade.offer.Offer;
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.locale.BSResources;
import io.bitsquare.locale.CurrencyUtil;
import io.bitsquare.offer.Offer;
import io.bitsquare.trade.BuyerAsTakerTrade;
import io.bitsquare.trade.SellerAsTakerTrade;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.states.TakerTradeState;
import org.bitcoinj.core.Address;
@ -173,13 +173,13 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
log.debug("offer state = " + state);
switch (state) {
case UNKNOWN:
case UNDEFINED:
// TODO set spinner?
break;
case AVAILABLE:
this.state.set(State.AMOUNT_SCREEN);
break;
case RESERVED:
case NOT_AVAILABLE:
if (takeOfferRequested)
errorMessage.set("Take offer request failed because offer is not available anymore. " +
"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.");
takeOfferRequested = false;
break;
case FAULT:
/* case FAULT:
if (takeOfferRequested)
errorMessage.set("Take offer request failed.");
else
errorMessage.set("The check for the offer availability failed.");
takeOfferRequested = false;
break;
break;*/
default:
log.error("Unhandled offer state: " + state);
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.DataModel;
import io.bitsquare.offer.Offer;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.TradeManager;
import io.bitsquare.user.User;
import io.bitsquare.trade.Tradable;
import io.bitsquare.trade.closed.ClosedTradableManager;
import io.bitsquare.trade.offer.Offer;
import com.google.inject.Inject;
@ -34,16 +33,14 @@ import javafx.collections.ObservableList;
class ClosedTradesDataModel implements Activatable, DataModel {
private final TradeManager tradeManager;
private final User user;
private final ClosedTradableManager closedTradableManager;
private final ObservableList<ClosedTradesListItem> list = FXCollections.observableArrayList();
private final ListChangeListener<Trade> tradesListChangeListener;
private final ListChangeListener<Tradable> tradesListChangeListener;
@Inject
public ClosedTradesDataModel(TradeManager tradeManager, User user) {
this.tradeManager = tradeManager;
this.user = user;
public ClosedTradesDataModel(ClosedTradableManager closedTradableManager) {
this.closedTradableManager = closedTradableManager;
tradesListChangeListener = change -> applyList();
}
@ -51,12 +48,12 @@ class ClosedTradesDataModel implements Activatable, DataModel {
@Override
public void activate() {
applyList();
tradeManager.getClosedTrades().addListener(tradesListChangeListener);
closedTradableManager.getClosedTrades().addListener(tradesListChangeListener);
}
@Override
public void deactivate() {
tradeManager.getClosedTrades().removeListener(tradesListChangeListener);
closedTradableManager.getClosedTrades().removeListener(tradesListChangeListener);
}
public ObservableList<ClosedTradesListItem> getList() {
@ -64,16 +61,16 @@ class ClosedTradesDataModel implements Activatable, DataModel {
}
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() {
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
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;
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.
*/
class ClosedTradesListItem {
private final Trade trade;
private final Tradable tradable;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
ClosedTradesListItem(Trade trade) {
this.trade = trade;
ClosedTradesListItem(Tradable tradable) {
this.tradable = tradable;
}
@ -40,7 +40,7 @@ class ClosedTradesListItem {
// Getters
///////////////////////////////////////////////////////////////////////////////////////////
Trade getTrade() {
return trade;
Tradable getTradable() {
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.ViewModel;
import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.trade.states.OffererTradeState;
import io.bitsquare.trade.states.TakerTradeState;
import io.bitsquare.trade.states.TradeState;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.offer.OpenOffer;
import com.google.inject.Inject;
import javafx.collections.ObservableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class ClosedTradesViewModel extends ActivatableWithDataModel<ClosedTradesDataModel> implements ViewModel {
private static final Logger log = LoggerFactory.getLogger(ClosedTradesViewModel.class);
private final BSFormatter formatter;
@ -45,34 +48,39 @@ class ClosedTradesViewModel extends ActivatableWithDataModel<ClosedTradesDataMod
}
String getTradeId(ClosedTradesListItem item) {
return item.getTrade().getId();
return item.getTradable().getId();
}
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) {
return (item != null) ? formatter.formatFiat(item.getTrade().getOffer().getPrice()) : "";
return (item != null) ? formatter.formatFiat(item.getTradable().getOffer().getPrice()) : "";
}
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) {
return (item != null) ? formatter.formatDirection(dataModel.getDirection(item.getTrade().getOffer())) : "";
return (item != null) ? formatter.formatDirection(dataModel.getDirection(item.getTradable().getOffer())) : "";
}
String getDate(ClosedTradesListItem item) {
return formatter.formatDateTime(item.getTrade().getDate());
return formatter.formatDateTime(item.getTradable().getDate());
}
String getState(ClosedTradesListItem item) {
if (item != null && item.getTrade() != null) {
TradeState.LifeCycleState lifeCycleState = item.getTrade().lifeCycleStateProperty().get();
if (lifeCycleState instanceof TakerTradeState.LifeCycleState) {
switch ((TakerTradeState.LifeCycleState) lifeCycleState) {
if (item != null) {
if (item.getTradable() instanceof Trade) {
switch (((Trade) item.getTradable()).lifeCycleStateProperty().get()) {
case COMPLETED:
return "Completed";
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.");
}
}
else if (lifeCycleState instanceof OffererTradeState.LifeCycleState) {
switch ((OffererTradeState.LifeCycleState) lifeCycleState) {
case OFFER_CANCELED:
else if (item.getTradable() instanceof OpenOffer) {
OpenOffer.State state = ((OpenOffer) item.getTradable()).getState();
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";
case COMPLETED:
return "Completed";
case FAILED:
return "Failed";
case OFFER_OPEN:
case PENDING:
throw new RuntimeException("That must not happen. We got a pending state but we are in the closed trades list.");
/* case FAILED:
return "Failed";*/
default:
log.error("Unhandled state {}", state);
return state.toString();
}
}
}

View File

@ -17,21 +17,24 @@
package io.bitsquare.gui.main.portfolio.openoffer;
import io.bitsquare.offer.Offer;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.offer.OpenOffer;
/**
* We could remove that wrapper if it is not needed for additional UI only fields.
*/
class OpenOfferListItem {
private final Offer offer;
private final OpenOffer openOffer;
public OpenOfferListItem(Trade trade) {
this.offer = trade.getOffer();
public OpenOfferListItem(OpenOffer openOffer) {
this.openOffer = openOffer;
}
public OpenOffer getOpenOffer() {
return openOffer;
}
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.gui.common.model.Activatable;
import io.bitsquare.gui.common.model.DataModel;
import io.bitsquare.offer.Offer;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.TradeManager;
import io.bitsquare.user.User;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.offer.OpenOffer;
import io.bitsquare.trade.offer.OpenOfferManager;
import com.google.inject.Inject;
@ -40,16 +39,14 @@ import org.slf4j.LoggerFactory;
class OpenOffersDataModel implements Activatable, DataModel {
private static final Logger log = LoggerFactory.getLogger(OpenOffersDataModel.class);
private final TradeManager tradeManager;
private final User user;
private final OpenOfferManager openOfferManager;
private final ObservableList<OpenOfferListItem> list = FXCollections.observableArrayList();
private final ListChangeListener<Trade> tradesListChangeListener;
private final ListChangeListener<OpenOffer> tradesListChangeListener;
@Inject
public OpenOffersDataModel(TradeManager tradeManager, User user) {
this.tradeManager = tradeManager;
this.user = user;
public OpenOffersDataModel(OpenOfferManager openOfferManager) {
this.openOfferManager = openOfferManager;
tradesListChangeListener = change -> applyList();
}
@ -57,16 +54,16 @@ class OpenOffersDataModel implements Activatable, DataModel {
@Override
public void activate() {
applyList();
tradeManager.getOpenOfferTrades().addListener(tradesListChangeListener);
openOfferManager.getOpenOffers().addListener(tradesListChangeListener);
}
@Override
public void deactivate() {
tradeManager.getOpenOfferTrades().removeListener(tradesListChangeListener);
openOfferManager.getOpenOffers().removeListener(tradesListChangeListener);
}
void onCancelOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
tradeManager.onCancelOpenOffer(offer, resultHandler, errorMessageHandler);
void onCancelOpenOffer(OpenOffer openOffer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
openOfferManager.onCancelOpenOffer(openOffer, resultHandler, errorMessageHandler);
}
@ -75,13 +72,13 @@ class OpenOffersDataModel implements Activatable, DataModel {
}
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() {
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
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.withdrawal.WithdrawalView;
import io.bitsquare.gui.util.GUIUtil;
import io.bitsquare.offer.Offer;
import io.bitsquare.trade.offer.OpenOffer;
import javax.inject.Inject;
@ -69,8 +69,8 @@ public class OpenOffersView extends ActivatableViewAndModel<GridPane, OpenOffers
table.setItems(model.getList());
}
private void onCancelOpenOffer(Offer offer) {
model.onCancelOpenOffer(offer,
private void onCancelOpenOffer(OpenOffer openOffer) {
model.onCancelOpenOffer(openOffer,
() -> {
log.debug("Remove offer was successful");
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);
if (item != null) {
button.setOnAction(event -> onCancelOpenOffer(item.getOffer()));
button.setOnAction(event -> onCancelOpenOffer(item.getOpenOffer()));
setGraphic(button);
}
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.ViewModel;
import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.offer.Offer;
import io.bitsquare.trade.offer.OpenOffer;
import com.google.inject.Inject;
@ -45,8 +45,8 @@ class OpenOffersViewModel extends ActivatableWithDataModel<OpenOffersDataModel>
}
void onCancelOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
dataModel.onCancelOpenOffer(offer, resultHandler, errorMessageHandler);
void onCancelOpenOffer(OpenOffer openOffer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
dataModel.onCancelOpenOffer(openOffer, resultHandler, errorMessageHandler);
}
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.portfolio.PortfolioView;
import io.bitsquare.gui.main.portfolio.closedtrades.ClosedTradesView;
import io.bitsquare.offer.Offer;
import io.bitsquare.trade.BuyerTrade;
import io.bitsquare.trade.Contract;
import io.bitsquare.trade.SellerTrade;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.TradeManager;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.states.TradeState;
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.CurrencyUtil;
import io.bitsquare.locale.LanguageUtil;
import io.bitsquare.offer.Offer;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.user.User;
import org.bitcoinj.core.Coin;