Use state for open offer

This commit is contained in:
Manfred Karrer 2015-03-21 22:58:06 +01:00
parent 5768b93cdf
commit f68030e488
10 changed files with 136 additions and 165 deletions

View File

@ -33,6 +33,7 @@ import org.bitcoinj.core.TransactionInput;
import org.bitcoinj.core.TransactionOutPoint; import org.bitcoinj.core.TransactionOutPoint;
import org.bitcoinj.core.TransactionOutput; import org.bitcoinj.core.TransactionOutput;
import org.bitcoinj.core.Utils; import org.bitcoinj.core.Utils;
import org.bitcoinj.core.VerificationException;
import org.bitcoinj.core.Wallet; import org.bitcoinj.core.Wallet;
import org.bitcoinj.crypto.TransactionSignature; import org.bitcoinj.crypto.TransactionSignature;
import org.bitcoinj.kits.WalletAppKit; import org.bitcoinj.kits.WalletAppKit;
@ -375,23 +376,17 @@ public class TradeWalletService {
Futures.addCallback(broadcastComplete, callback); Futures.addCallback(broadcastComplete, callback);
} }
// Returns local transaction which has a different state as the serialized depositTx we get from the offerer // Returns local transaction which has a different state as the serialized publishedOffererDepositTx we get from the offerer
public Transaction takerCommitsDepositTx(Transaction depositTx) throws WalletException { public Transaction takerCommitsDepositTx(Transaction publishedOffererDepositTx) throws VerificationException {
log.trace("takerCommitsDepositTx called"); log.trace("takerCommitsDepositTx called");
log.trace("depositTx " + depositTx.toString()); log.trace("publishedOffererDepositTx " + publishedOffererDepositTx.toString());
// We need to recreate the tx we get a null pointer otherwise // We need to recreate the tx we get a null pointer otherwise
Transaction localDepositTx = new Transaction(params, depositTx.bitcoinSerialize()); Transaction depositTx = new Transaction(params, publishedOffererDepositTx.bitcoinSerialize());
log.trace("depositTx " + depositTx.toString());
try { wallet.receivePending(depositTx, null, true);
// TODO check if that is correct return depositTx;
wallet.receivePending(depositTx, null, true);
} catch (Throwable t) {
log.error(t.getMessage());
t.printStackTrace();
throw new WalletException(t);
}
return localDepositTx;
} }
public byte[] offererCreatesAndSignsPayoutTx(Transaction depositTx, public byte[] offererCreatesAndSignsPayoutTx(Transaction depositTx,

View File

@ -112,7 +112,7 @@ class PendingTradesDataModel implements Activatable, DataModel {
sortList(); sortList();
// select either currentPendingTrade or first in the list // select either currentPendingTrade or first in the list
if (tradeManager.getCurrentPendingTrade() != null) { /*if (tradeManager.getCurrentPendingTrade() != null) {
for (int i = 0; i < list.size(); i++) { for (int i = 0; i < list.size(); i++) {
PendingTradesListItem item = list.get(i); PendingTradesListItem item = list.get(i);
if (tradeManager.getCurrentPendingTrade().getId().equals(item.getTrade().getId())) { if (tradeManager.getCurrentPendingTrade().getId().equals(item.getTrade().getId())) {
@ -122,7 +122,8 @@ class PendingTradesDataModel implements Activatable, DataModel {
} }
} }
} }
else if (list.size() > 0) { else */
if (list.size() > 0) {
selectTrade(list.get(0)); selectTrade(list.get(0));
selectedIndex.set(0); selectedIndex.set(0);
} }

View File

@ -113,8 +113,8 @@ class OfferBookDataModel implements Activatable, DataModel {
btcCode.unbind(); btcCode.unbind();
} }
void removeOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { void cancelOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
tradeManager.removeOpenOffer(offer, resultHandler, errorMessageHandler); tradeManager.cancelOpenOffer(offer, resultHandler, errorMessageHandler);
} }
void calculateVolume() { void calculateVolume() {

View File

@ -471,7 +471,7 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
if (model.isMyOffer(offer)) { if (model.isMyOffer(offer)) {
iconView.setId("image-remove"); iconView.setId("image-remove");
title = "Remove"; title = "Remove";
button.setOnAction(event -> model.removeOpenOffer(item button.setOnAction(event -> model.cancelOpenOffer(item
.getOffer())); .getOffer()));
} }
else { else {

View File

@ -102,8 +102,8 @@ class OfferBookViewModel extends ActivatableWithDataModel<OfferBookDataModel> im
(newValue))); (newValue)));
} }
void removeOpenOffer(Offer offer) { void cancelOpenOffer(Offer offer) {
dataModel.removeOpenOffer(offer, dataModel.cancelOpenOffer(offer,
() -> { () -> {
// visual feedback? // visual feedback?
log.debug("Remove offer was successful"); log.debug("Remove offer was successful");

View File

@ -46,7 +46,7 @@ public class Trade implements Serializable {
COMPLETED, COMPLETED,
FAILED FAILED
} }
public static enum ProcessState { public static enum ProcessState {
INIT, INIT,
TAKE_OFFER_FEE_PUBLISH_FAILED, TAKE_OFFER_FEE_PUBLISH_FAILED,
@ -74,6 +74,8 @@ public class Trade implements Serializable {
private final Offer offer; private final Offer offer;
private final Date date; private final Date date;
private ProcessState processState; private ProcessState processState;
private LifeCycleState lifeCycleState;
private Coin tradeAmount; private Coin tradeAmount;
private Contract contract; private Contract contract;
private String contractAsJson; private String contractAsJson;
@ -88,7 +90,8 @@ public class Trade implements Serializable {
// access. Only use the accessor not the private field. // access. Only use the accessor not the private field.
transient private ObjectProperty<Coin> _tradeAmount; transient private ObjectProperty<Coin> _tradeAmount;
transient private ObjectProperty<Fiat> _tradeVolume; transient private ObjectProperty<Fiat> _tradeVolume;
transient private ObjectProperty<ProcessState> _state; transient private ObjectProperty<ProcessState> _processState;
transient private ObjectProperty<LifeCycleState> _lifeCycleState;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -154,6 +157,10 @@ public class Trade implements Serializable {
processStateProperty().set(processState); processStateProperty().set(processState);
} }
public void setLifeCycleState(LifeCycleState lifeCycleState) {
this.lifeCycleState = lifeCycleState;
lifeCycleStateProperty().set(lifeCycleState);
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Getters // Getters
@ -183,6 +190,10 @@ public class Trade implements Serializable {
return processState; return processState;
} }
public LifeCycleState getLifeCycleState() {
return lifeCycleState;
}
public Coin getSecurityDeposit() { public Coin getSecurityDeposit() {
return offer.getSecurityDeposit(); return offer.getSecurityDeposit();
} }
@ -224,10 +235,17 @@ public class Trade implements Serializable {
} }
public ObjectProperty<ProcessState> processStateProperty() { public ObjectProperty<ProcessState> processStateProperty() {
if (_state == null) if (_processState == null)
_state = new SimpleObjectProperty<>(processState); _processState = new SimpleObjectProperty<>(processState);
return _state; return _processState;
}
public ObjectProperty<LifeCycleState> lifeCycleStateProperty() {
if (_lifeCycleState == null)
_lifeCycleState = new SimpleObjectProperty<>(lifeCycleState);
return _lifeCycleState;
} }
@Override @Override

View File

@ -31,17 +31,13 @@ import io.bitsquare.p2p.AddressService;
import io.bitsquare.p2p.EncryptedMailboxMessage; import io.bitsquare.p2p.EncryptedMailboxMessage;
import io.bitsquare.p2p.MailboxMessage; import io.bitsquare.p2p.MailboxMessage;
import io.bitsquare.p2p.MailboxService; import io.bitsquare.p2p.MailboxService;
import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.MessageService; import io.bitsquare.p2p.MessageService;
import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.Peer;
import io.bitsquare.p2p.listener.SendMessageListener;
import io.bitsquare.persistence.Persistence; import io.bitsquare.persistence.Persistence;
import io.bitsquare.trade.handlers.TradeResultHandler; import io.bitsquare.trade.handlers.TradeResultHandler;
import io.bitsquare.trade.handlers.TransactionResultHandler; import io.bitsquare.trade.handlers.TransactionResultHandler;
import io.bitsquare.trade.protocol.availability.CheckOfferAvailabilityModel; import io.bitsquare.trade.protocol.availability.CheckOfferAvailabilityModel;
import io.bitsquare.trade.protocol.availability.CheckOfferAvailabilityProtocol; import io.bitsquare.trade.protocol.availability.CheckOfferAvailabilityProtocol;
import io.bitsquare.trade.protocol.availability.messages.ReportOfferAvailabilityMessage;
import io.bitsquare.trade.protocol.availability.messages.RequestIsOfferAvailableMessage;
import io.bitsquare.trade.protocol.placeoffer.PlaceOfferModel; import io.bitsquare.trade.protocol.placeoffer.PlaceOfferModel;
import io.bitsquare.trade.protocol.placeoffer.PlaceOfferProtocol; import io.bitsquare.trade.protocol.placeoffer.PlaceOfferProtocol;
import io.bitsquare.trade.protocol.trade.messages.TradeMessage; import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
@ -69,8 +65,6 @@ import javafx.collections.ObservableMap;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import static com.google.common.base.Preconditions.checkNotNull;
public class TradeManager { public class TradeManager {
private static final Logger log = LoggerFactory.getLogger(TradeManager.class); private static final Logger log = LoggerFactory.getLogger(TradeManager.class);
@ -90,12 +84,9 @@ public class TradeManager {
private final Map<String, OffererAsBuyerProtocol> offererAsBuyerProtocolMap = new HashMap<>(); private final Map<String, OffererAsBuyerProtocol> offererAsBuyerProtocolMap = new HashMap<>();
private final Map<String, CheckOfferAvailabilityProtocol> checkOfferAvailabilityProtocolMap = new HashMap<>(); private final Map<String, CheckOfferAvailabilityProtocol> checkOfferAvailabilityProtocolMap = new HashMap<>();
private final ObservableMap<String, Trade> openOfferTrades = FXCollections.observableHashMap(); private final Map<String, Trade> trades = new HashMap<>();
private final ObservableMap<String, Trade> pendingTrades = FXCollections.observableHashMap();
private final ObservableMap<String, Trade> closedTrades = FXCollections.observableHashMap();
private final Map<String, MailboxMessage> mailboxMessages = new HashMap<>();
private Trade currentPendingTrade; private final Map<String, MailboxMessage> mailboxMessages = new HashMap<>();
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -119,19 +110,9 @@ public class TradeManager {
this.encryptionService = encryptionService; this.encryptionService = encryptionService;
this.offerBookService = offerBookService; this.offerBookService = offerBookService;
Serializable openOfferTradesObject = persistence.read(this, "openOfferTrades"); Serializable tradesObject = persistence.read(this, "trades");
if (openOfferTradesObject instanceof Map<?, ?>) { if (tradesObject instanceof Map<?, ?>) {
openOfferTrades.putAll((Map<String, Trade>) openOfferTradesObject); trades.putAll((Map<String, Trade>) tradesObject);
}
Serializable pendingTradesObject = persistence.read(this, "pendingTrades");
if (pendingTradesObject instanceof Map<?, ?>) {
pendingTrades.putAll((Map<String, Trade>) pendingTradesObject);
}
Serializable closedTradesObject = persistence.read(this, "closedTrades");
if (closedTradesObject instanceof Map<?, ?>) {
closedTrades.putAll((Map<String, Trade>) closedTradesObject);
} }
} }
@ -143,10 +124,7 @@ public class TradeManager {
// When all services are initialized we create the protocols for our open offers and persisted not completed pendingTrades // When all services are initialized we create the protocols for our open offers and persisted not completed pendingTrades
// BuyerAcceptsOfferProtocol listens for take offer requests, so we need to instantiate it early. // BuyerAcceptsOfferProtocol listens for take offer requests, so we need to instantiate it early.
public void onAllServicesInitialized() { public void onAllServicesInitialized() {
for (Map.Entry<String, Trade> entry : openOfferTrades.entrySet()) { for (Map.Entry<String, Trade> entry : trades.entrySet()) {
createOffererAsBuyerProtocol(entry.getValue());
}
for (Map.Entry<String, Trade> entry : pendingTrades.entrySet()) {
// We continue an interrupted trade. // We continue an interrupted trade.
// TODO if the peer has changed its IP address, we need to make another findPeer request. At the moment we use the peer stored in trade to // TODO if the peer has changed its IP address, we need to make another findPeer request. At the moment we use the peer stored in trade to
// continue the trade, but that might fail. // continue the trade, but that might fail.
@ -164,8 +142,6 @@ public class TradeManager {
decryptMailboxMessages(encryptedMailboxMessages); decryptMailboxMessages(encryptedMailboxMessages);
emptyMailbox(); emptyMailbox();
}); });
messageService.addMessageHandler(this::handleMessage);
} }
public boolean isMyOffer(Offer offer) { public boolean isMyOffer(Offer offer) {
@ -206,9 +182,8 @@ public class TradeManager {
PlaceOfferProtocol placeOfferProtocol = new PlaceOfferProtocol( PlaceOfferProtocol placeOfferProtocol = new PlaceOfferProtocol(
model, model,
(transaction) -> { (transaction) -> {
Trade trade = new Trade(offer); Trade trade = createTrade(offer);
openOfferTrades.put(trade.getId(), trade); trade.setLifeCycleState(Trade.LifeCycleState.OPEN_OFFER);
persistOpenOffers();
createOffererAsBuyerProtocol(trade); createOffererAsBuyerProtocol(trade);
resultHandler.handleResult(transaction); resultHandler.handleResult(transaction);
}, },
@ -218,6 +193,11 @@ public class TradeManager {
placeOfferProtocol.placeOffer(); placeOfferProtocol.placeOffer();
} }
public void cancelOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
trades.get(offer.getId()).setLifeCycleState(Trade.LifeCycleState.CANCELED);
removeOpenOffer(offer, resultHandler, errorMessageHandler, true);
}
public void removeOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { public void removeOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
removeOpenOffer(offer, resultHandler, errorMessageHandler, true); removeOpenOffer(offer, resultHandler, errorMessageHandler, true);
} }
@ -274,7 +254,7 @@ public class TradeManager {
// TODO remove if check when persistence is impl. // TODO remove if check when persistence is impl.
if (offererAsBuyerProtocolMap.containsKey(tradeId)) { if (offererAsBuyerProtocolMap.containsKey(tradeId)) {
offererAsBuyerProtocolMap.get(tradeId).onFiatPaymentStarted(); offererAsBuyerProtocolMap.get(tradeId).onFiatPaymentStarted();
persistPendingTrades(); persistTrades();
} }
} }
@ -283,7 +263,8 @@ public class TradeManager {
} }
public void onWithdrawAtTradeCompleted(Trade trade) { public void onWithdrawAtTradeCompleted(Trade trade) {
closeTrade(trade); trade.setLifeCycleState(Trade.LifeCycleState.COMPLETED);
removeFromProtocolMap(trade);
} }
@ -296,51 +277,27 @@ public class TradeManager {
} }
///////////////////////////////////////////////////////////////////////////////////////////
// Message handler
///////////////////////////////////////////////////////////////////////////////////////////
// Offerer handles those requests
private void handleMessage(Message message, Peer sender) {
if (message instanceof RequestIsOfferAvailableMessage) {
String offerId = ((RequestIsOfferAvailableMessage) message).offerId;
checkNotNull(offerId);
ReportOfferAvailabilityMessage reportOfferAvailabilityMessage = new ReportOfferAvailabilityMessage(offerId, isOfferOpen(offerId));
messageService.sendMessage(sender, reportOfferAvailabilityMessage, new SendMessageListener() {
@Override
public void handleResult() {
// Offerer does not do anything at that moment. Peer might only watch the offer and does nto start a trade.
log.trace("ReportOfferAvailabilityMessage successfully arrived at peer");
}
@Override
public void handleFault() {
log.warn("Sending ReportOfferAvailabilityMessage failed.");
}
});
}
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Getters // Getters
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public ObservableMap<String, Trade> getOpenOfferTrades() { public ObservableMap<String, Trade> getOpenOfferTrades() {
return openOfferTrades; ObservableMap<String, Trade> list = FXCollections.observableHashMap();
list.putAll(trades);
return list;
} }
public ObservableMap<String, Trade> getPendingTrades() { public ObservableMap<String, Trade> getPendingTrades() {
return pendingTrades; ObservableMap<String, Trade> list = FXCollections.observableHashMap();
list.putAll(trades);
return list;
} }
public ObservableMap<String, Trade> getClosedTrades() { public ObservableMap<String, Trade> getClosedTrades() {
return closedTrades; ObservableMap<String, Trade> list = FXCollections.observableHashMap();
} list.putAll(trades);
return list;
public Trade getCurrentPendingTrade() {
return currentPendingTrade;
} }
@ -359,21 +316,17 @@ public class TradeManager {
String offerId = offer.getId(); String offerId = offer.getId();
offerBookService.removeOffer(offer, offerBookService.removeOffer(offer,
() -> { () -> {
if (openOfferTrades.containsKey(offerId)) { offer.setState(Offer.State.REMOVED);
openOfferTrades.remove(offerId); trades.remove(offerId);
disposeCheckOfferAvailabilityRequest(offer);
persistOpenOffers();
if (removeFromBuyerAcceptsOfferProtocolMap && offererAsBuyerProtocolMap.containsKey(offerId)) {
offererAsBuyerProtocolMap.get(offerId).cleanup();
offererAsBuyerProtocolMap.remove(offerId);
}
resultHandler.handleResult(); disposeCheckOfferAvailabilityRequest(offer);
} persistTrades();
else { if (removeFromBuyerAcceptsOfferProtocolMap && offererAsBuyerProtocolMap.containsKey(offerId)) {
log.error("Locally stored offers does not contain the offer with the ID " + offerId); offererAsBuyerProtocolMap.get(offerId).cleanup();
errorMessageHandler.handleErrorMessage("Locally stored offers does not contain the offer with the ID " + offerId); offererAsBuyerProtocolMap.remove(offerId);
} }
resultHandler.handleResult();
}, },
(message, throwable) -> errorMessageHandler.handleErrorMessage(message)); (message, throwable) -> errorMessageHandler.handleErrorMessage(message));
} }
@ -408,15 +361,9 @@ public class TradeManager {
} }
private Trade createTrade(Offer offer) { private Trade createTrade(Offer offer) {
if (pendingTrades.containsKey(offer.getId()))
log.error("That must never happen: Trades contains already an trade with the ID " + offer.getId());
Trade trade = new Trade(offer); Trade trade = new Trade(offer);
pendingTrades.put(offer.getId(), trade); trades.put(trade.getId(), trade);
persistPendingTrades(); persistTrades();
currentPendingTrade = trade;
return trade; return trade;
} }
@ -432,11 +379,12 @@ public class TradeManager {
case FIAT_PAYMENT_STARTED: case FIAT_PAYMENT_STARTED:
case FIAT_PAYMENT_RECEIVED: case FIAT_PAYMENT_RECEIVED:
case PAYOUT_PUBLISHED: case PAYOUT_PUBLISHED:
persistPendingTrades(); persistTrades();
break; break;
case MESSAGE_SENDING_FAILED: case MESSAGE_SENDING_FAILED:
case FAULT: case FAULT:
closeTrade(trade); trade.setLifeCycleState(Trade.LifeCycleState.FAILED);
removeFromProtocolMap(trade);
break; break;
default: default:
log.warn("Unhandled trade state: " + newValue); log.warn("Unhandled trade state: " + newValue);
@ -484,30 +432,25 @@ public class TradeManager {
case INIT: case INIT:
break; break;
case TAKE_OFFER_FEE_TX_CREATED: case TAKE_OFFER_FEE_TX_CREATED:
persistPendingTrades(); persistTrades();
break; break;
case DEPOSIT_PUBLISHED: case DEPOSIT_PUBLISHED:
removeOpenOffer(trade.getOffer(), removeOpenOffer(trade.getOffer(),
() -> log.debug("remove offer was successful"), () -> log.debug("remove offer was successful"),
(message) -> log.error(message), (message) -> log.error(message),
false); false);
// after we have published the deposit tx we add that trade to the pendingTrades
if (pendingTrades.containsKey(trade.getId()))
log.error("That must never happen: Trades contains already an trade with the ID " + trade.getId());
pendingTrades.put(trade.getId(), trade);
persistPendingTrades();
break; break;
case DEPOSIT_CONFIRMED: case DEPOSIT_CONFIRMED:
case FIAT_PAYMENT_STARTED: case FIAT_PAYMENT_STARTED:
case FIAT_PAYMENT_RECEIVED: case FIAT_PAYMENT_RECEIVED:
case PAYOUT_PUBLISHED: case PAYOUT_PUBLISHED:
persistPendingTrades(); persistTrades();
break; break;
case TAKE_OFFER_FEE_PUBLISH_FAILED: case TAKE_OFFER_FEE_PUBLISH_FAILED:
case MESSAGE_SENDING_FAILED: case MESSAGE_SENDING_FAILED:
case FAULT: case FAULT:
closeTrade(trade); trade.setLifeCycleState(Trade.LifeCycleState.FAILED);
removeFromProtocolMap(trade);
break; break;
default: default:
log.warn("Unhandled trade state: " + newValue); log.warn("Unhandled trade state: " + newValue);
@ -523,12 +466,7 @@ public class TradeManager {
} }
} }
private void closeTrade(Trade trade) { private void removeFromProtocolMap(Trade trade) {
if (pendingTrades.containsKey(trade.getId())) {
pendingTrades.remove(trade.getId());
persistPendingTrades();
}
if (takerAsSellerProtocolMap.containsKey(trade.getId())) { if (takerAsSellerProtocolMap.containsKey(trade.getId())) {
takerAsSellerProtocolMap.get(trade.getId()).cleanup(); takerAsSellerProtocolMap.get(trade.getId()).cleanup();
takerAsSellerProtocolMap.remove(trade.getId()); takerAsSellerProtocolMap.remove(trade.getId());
@ -537,11 +475,6 @@ public class TradeManager {
offererAsBuyerProtocolMap.get(trade.getId()).cleanup(); offererAsBuyerProtocolMap.get(trade.getId()).cleanup();
offererAsBuyerProtocolMap.remove(trade.getId()); offererAsBuyerProtocolMap.remove(trade.getId());
} }
if (!closedTrades.containsKey(trade.getId())) {
closedTrades.put(trade.getId(), trade);
persistClosedTrades();
}
} }
@ -587,21 +520,8 @@ public class TradeManager {
}); });
} }
boolean isOfferOpen(String offerId) { private void persistTrades() {
return openOfferTrades.containsKey(offerId) persistence.write(this, "trades", (Map<String, Trade>) new HashMap<>(trades));
&& (openOfferTrades.get(offerId).getOffer().getState() == Offer.State.UNKNOWN
|| openOfferTrades.get(offerId).getOffer().getState() == Offer.State.AVAILABLE);
} }
private void persistOpenOffers() {
persistence.write(this, "openOfferTrades", (Map<String, Trade>) new HashMap<>(openOfferTrades));
}
private void persistPendingTrades() {
persistence.write(this, "pendingTrades", (Map<String, Trade>) new HashMap<>(pendingTrades));
}
private void persistClosedTrades() {
persistence.write(this, "closedTrades", (Map<String, Trade>) new HashMap<>(closedTrades));
}
} }

View File

@ -17,15 +17,15 @@
package io.bitsquare.trade.protocol.availability.messages; package io.bitsquare.trade.protocol.availability.messages;
import io.bitsquare.trade.protocol.trade.messages.OfferMessage; import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
import java.io.Serializable; import java.io.Serializable;
public class RequestIsOfferAvailableMessage extends OfferMessage implements Serializable { public class RequestIsOfferAvailableMessage extends TradeMessage implements Serializable {
private static final long serialVersionUID = 4630151440192191798L; private static final long serialVersionUID = 4630151440192191798L;
public RequestIsOfferAvailableMessage(String offerId) { public RequestIsOfferAvailableMessage(String tradeId) {
super.offerId = offerId; super.tradeId = tradeId;
} }

View File

@ -22,6 +22,10 @@ import io.bitsquare.p2p.MailboxMessage;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.MessageHandler; import io.bitsquare.p2p.MessageHandler;
import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.Peer;
import io.bitsquare.p2p.listener.SendMessageListener;
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.PayoutTxPublishedMessage; import io.bitsquare.trade.protocol.trade.messages.PayoutTxPublishedMessage;
import io.bitsquare.trade.protocol.trade.messages.RequestDepositTxInputsMessage; import io.bitsquare.trade.protocol.trade.messages.RequestDepositTxInputsMessage;
import io.bitsquare.trade.protocol.trade.messages.RequestOffererPublishDepositTxMessage; import io.bitsquare.trade.protocol.trade.messages.RequestOffererPublishDepositTxMessage;
@ -96,13 +100,42 @@ public class OffererAsBuyerProtocol {
// Incoming message handling // Incoming message handling
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// OpenOffer requests
private void handleRequestIsOfferAvailableMessage(RequestIsOfferAvailableMessage tradeMessage, Peer sender) {
try {
checkTradeId(model.id, tradeMessage);
// We don't store anything in the model 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 = model.trade.getLifeCycleState() == Trade.LifeCycleState.OPEN_OFFER;
ReportOfferAvailabilityMessage reportOfferAvailabilityMessage = new ReportOfferAvailabilityMessage(model.id, isOfferOpen);
model.messageService.sendMessage(sender, 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() {
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
private void handleRequestDepositTxInputsMessage(RequestDepositTxInputsMessage tradeMessage, Peer taker) { private void handleRequestDepositTxInputsMessage(RequestDepositTxInputsMessage tradeMessage, Peer taker) {
checkTradeId(model.id, tradeMessage); checkTradeId(model.id, tradeMessage);
model.setTradeMessage(tradeMessage); model.setTradeMessage(tradeMessage);
model.trade.setTradingPeer(taker); model.trade.setTradingPeer(taker);
TaskRunner<OffererAsBuyerModel> taskRunner = new TaskRunner<>(model, TaskRunner<OffererAsBuyerModel> taskRunner = new TaskRunner<>(model,
() -> log.debug("sequence at handleTakeOfferFeePayedMessage completed"), () -> log.debug("taskRunner at handleTakeOfferFeePayedMessage completed"),
(errorMessage) -> handleTaskRunnerFault(errorMessage)); (errorMessage) -> handleTaskRunnerFault(errorMessage));
taskRunner.addTasks( taskRunner.addTasks(
ProcessRequestDepositTxInputsMessage.class, ProcessRequestDepositTxInputsMessage.class,
@ -137,7 +170,7 @@ public class OffererAsBuyerProtocol {
// User clicked the "bank transfer started" button // User clicked the "bank transfer started" button
public void onFiatPaymentStarted() { public void onFiatPaymentStarted() {
TaskRunner<OffererAsBuyerModel> taskRunner = new TaskRunner<>(model, TaskRunner<OffererAsBuyerModel> taskRunner = new TaskRunner<>(model,
() -> log.debug("sequence at handleBankTransferStartedUIEvent completed"), () -> log.debug("taskRunner at handleBankTransferStartedUIEvent completed"),
(errorMessage) -> handleTaskRunnerFault(errorMessage)); (errorMessage) -> handleTaskRunnerFault(errorMessage));
taskRunner.addTasks( taskRunner.addTasks(
CreateAndSignPayoutTx.class, CreateAndSignPayoutTx.class,
@ -157,7 +190,7 @@ public class OffererAsBuyerProtocol {
TaskRunner<OffererAsBuyerModel> taskRunner = new TaskRunner<>(model, TaskRunner<OffererAsBuyerModel> taskRunner = new TaskRunner<>(model,
() -> { () -> {
log.debug("sequence at handlePayoutTxPublishedMessage completed"); log.debug("taskRunner at handlePayoutTxPublishedMessage completed");
// we are done! // we are done!
model.onComplete(); model.onComplete();
}, },
@ -179,10 +212,12 @@ public class OffererAsBuyerProtocol {
nonEmptyStringOf(tradeMessage.tradeId); nonEmptyStringOf(tradeMessage.tradeId);
if (tradeMessage.tradeId.equals(model.id)) { if (tradeMessage.tradeId.equals(model.id)) {
if (tradeMessage instanceof RequestDepositTxInputsMessage) { if (tradeMessage instanceof RequestIsOfferAvailableMessage) {
handleRequestIsOfferAvailableMessage((RequestIsOfferAvailableMessage) tradeMessage, sender);
}
else if (tradeMessage instanceof RequestDepositTxInputsMessage) {
handleRequestDepositTxInputsMessage((RequestDepositTxInputsMessage) tradeMessage, sender); handleRequestDepositTxInputsMessage((RequestDepositTxInputsMessage) tradeMessage, sender);
} }
else if (tradeMessage instanceof RequestOffererPublishDepositTxMessage) { else if (tradeMessage instanceof RequestOffererPublishDepositTxMessage) {
handleRequestOffererPublishDepositTxMessage((RequestOffererPublishDepositTxMessage) tradeMessage); handleRequestOffererPublishDepositTxMessage((RequestOffererPublishDepositTxMessage) tradeMessage);
} }

View File

@ -22,6 +22,7 @@ import io.bitsquare.common.taskrunner.TaskRunner;
import io.bitsquare.trade.protocol.trade.taker.models.TakerAsSellerModel; import io.bitsquare.trade.protocol.trade.taker.models.TakerAsSellerModel;
import org.bitcoinj.core.Transaction; import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.VerificationException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -36,12 +37,13 @@ public class TakerCommitDepositTx extends Task<TakerAsSellerModel> {
@Override @Override
protected void doRun() { protected void doRun() {
try { try {
// We need to put the tx into our wallet to have a fully setup tx // To access tx confidence we need to add that tx into our wallet.
Transaction localDepositTx = model.tradeWalletService.takerCommitsDepositTx(model.trade.getDepositTx()); Transaction depositTx = model.tradeWalletService.takerCommitsDepositTx(model.trade.getDepositTx());
model.trade.setDepositTx(localDepositTx);
model.trade.setDepositTx(depositTx);
complete(); complete();
} catch (Throwable t) { } catch (VerificationException t) {
failed(t); failed(t);
} }
} }