Improve state handling of trade

This commit is contained in:
Manfred Karrer 2015-03-21 21:51:13 +01:00
parent 6099f36b53
commit 5768b93cdf
32 changed files with 179 additions and 249 deletions

View file

@ -375,12 +375,13 @@ public class TradeWalletService {
Futures.addCallback(broadcastComplete, callback); Futures.addCallback(broadcastComplete, callback);
} }
public void takerCommitsDepositTx(Transaction depositTx) throws WalletException { // Returns local transaction which has a different state as the serialized depositTx we get from the offerer
public Transaction takerCommitsDepositTx(Transaction depositTx) throws WalletException {
log.trace("takerCommitsDepositTx called"); log.trace("takerCommitsDepositTx called");
log.trace("depositTx " + depositTx.toString()); log.trace("depositTx " + depositTx.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
depositTx = new Transaction(params, depositTx.bitcoinSerialize()); Transaction localDepositTx = new Transaction(params, depositTx.bitcoinSerialize());
try { try {
// TODO check if that is correct // TODO check if that is correct
@ -390,6 +391,7 @@ public class TradeWalletService {
t.printStackTrace(); t.printStackTrace();
throw new WalletException(t); throw new WalletException(t);
} }
return localDepositTx;
} }
public byte[] offererCreatesAndSignsPayoutTx(Transaction depositTx, public byte[] offererCreatesAndSignsPayoutTx(Transaction depositTx,

View file

@ -18,6 +18,7 @@
package io.bitsquare.gui.main.portfolio.offer; package io.bitsquare.gui.main.portfolio.offer;
import io.bitsquare.offer.Offer; import io.bitsquare.offer.Offer;
import io.bitsquare.trade.Trade;
/** /**
* We could remove that wrapper if it is not needed for additional UI only fields. * We could remove that wrapper if it is not needed for additional UI only fields.
@ -26,8 +27,8 @@ class OfferListItem {
private final Offer offer; private final Offer offer;
public OfferListItem(Offer offer) { public OfferListItem(Trade trade) {
this.offer = offer; this.offer = trade.getOffer();
} }
public Offer getOffer() { public Offer getOffer() {

View file

@ -23,6 +23,7 @@ import io.bitsquare.common.viewfx.model.Activatable;
import io.bitsquare.common.viewfx.model.DataModel; import io.bitsquare.common.viewfx.model.DataModel;
import io.bitsquare.offer.Direction; import io.bitsquare.offer.Direction;
import io.bitsquare.offer.Offer; import io.bitsquare.offer.Offer;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.TradeManager; import io.bitsquare.trade.TradeManager;
import io.bitsquare.user.User; import io.bitsquare.user.User;
@ -44,7 +45,7 @@ class OffersDataModel implements Activatable, DataModel {
private final User user; private final User user;
private final ObservableList<OfferListItem> list = FXCollections.observableArrayList(); private final ObservableList<OfferListItem> list = FXCollections.observableArrayList();
private final MapChangeListener<String, Offer> offerMapChangeListener; private final MapChangeListener<String, Trade> offerMapChangeListener;
@Inject @Inject
@ -63,17 +64,17 @@ class OffersDataModel implements Activatable, DataModel {
@Override @Override
public void activate() { public void activate() {
list.clear(); list.clear();
list.addAll(tradeManager.getOpenOffers().values().stream().map(OfferListItem::new).collect(Collectors.toList())); list.addAll(tradeManager.getOpenOfferTrades().values().stream().map(OfferListItem::new).collect(Collectors.toList()));
// we sort by date, earliest first // we sort by date, earliest first
list.sort((o1, o2) -> o2.getOffer().getCreationDate().compareTo(o1.getOffer().getCreationDate())); list.sort((o1, o2) -> o2.getOffer().getCreationDate().compareTo(o1.getOffer().getCreationDate()));
tradeManager.getOpenOffers().addListener(offerMapChangeListener); tradeManager.getOpenOfferTrades().addListener(offerMapChangeListener);
} }
@Override @Override
public void deactivate() { public void deactivate() {
tradeManager.getOpenOffers().removeListener(offerMapChangeListener); tradeManager.getOpenOfferTrades().removeListener(offerMapChangeListener);
} }
void removeOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { void removeOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {

View file

@ -66,11 +66,11 @@ class PendingTradesDataModel implements Activatable, DataModel {
private boolean isOfferer; private boolean isOfferer;
private Trade closedTrade; private Trade closedTrade;
private final ChangeListener<Trade.State> tradeStateChangeListener; private final ChangeListener<Trade.ProcessState> tradeStateChangeListener;
private final MapChangeListener<String, Trade> mapChangeListener; private final MapChangeListener<String, Trade> mapChangeListener;
final StringProperty txId = new SimpleStringProperty(); final StringProperty txId = new SimpleStringProperty();
final ObjectProperty<Trade.State> tradeState = new SimpleObjectProperty<>(); final ObjectProperty<Trade.ProcessState> tradeState = new SimpleObjectProperty<>();
final IntegerProperty selectedIndex = new SimpleIntegerProperty(-1); final IntegerProperty selectedIndex = new SimpleIntegerProperty(-1);
@Inject @Inject
@ -146,9 +146,9 @@ class PendingTradesDataModel implements Activatable, DataModel {
isOfferer = getTrade().getOffer().getP2PSigPubKey().equals(user.getP2PSigPubKey()); isOfferer = getTrade().getOffer().getP2PSigPubKey().equals(user.getP2PSigPubKey());
Trade trade = getTrade(); Trade trade = getTrade();
trade.stateProperty().addListener(tradeStateChangeListener); trade.processStateProperty().addListener(tradeStateChangeListener);
tradeState.set(trade.stateProperty().get()); tradeState.set(trade.processStateProperty().get());
log.trace("selectTrade trade.stateProperty().get() " + trade.stateProperty().get()); log.trace("selectTrade trade.stateProperty().get() " + trade.processStateProperty().get());
if (trade.getDepositTx() != null) if (trade.getDepositTx() != null)
txId.set(trade.getDepositTx().getHashAsString()); txId.set(trade.getDepositTx().getHashAsString());
@ -277,7 +277,7 @@ class PendingTradesDataModel implements Activatable, DataModel {
private void cleanUpSelectedTrade() { private void cleanUpSelectedTrade() {
if (selectedItem != null) { if (selectedItem != null) {
selectedItem.getTrade().stateProperty().removeListener(tradeStateChangeListener); selectedItem.getTrade().processStateProperty().removeListener(tradeStateChangeListener);
} }
} }

View file

@ -181,6 +181,7 @@ public class PendingTradesView extends ActivatableViewAndModel<AnchorPane, Pendi
@FXML @FXML
void onPaymentStarted() { void onPaymentStarted() {
model.fiatPaymentStarted(); model.fiatPaymentStarted();
confirmPaymentReceiptButton.setDisable(true);
} }
@FXML @FXML

View file

@ -218,10 +218,10 @@ class PendingTradesViewModel extends ActivatableWithDataModel<PendingTradesDataM
private void updateState() { private void updateState() {
Trade.State tradeState = dataModel.tradeState.get(); Trade.ProcessState tradeProcessState = dataModel.tradeState.get();
log.trace("tradeState " + tradeState); log.trace("tradeState " + tradeProcessState);
if (tradeState != null) { if (tradeProcessState != null) {
switch (tradeState) { switch (tradeProcessState) {
case DEPOSIT_PUBLISHED: case DEPOSIT_PUBLISHED:
state.set(dataModel.isOfferer() ? State.OFFERER_BUYER_WAIT_TX_CONF : State.TAKER_SELLER_WAIT_TX_CONF); state.set(dataModel.isOfferer() ? State.OFFERER_BUYER_WAIT_TX_CONF : State.TAKER_SELLER_WAIT_TX_CONF);
break; break;
@ -240,7 +240,7 @@ class PendingTradesViewModel extends ActivatableWithDataModel<PendingTradesDataM
// TODO error states not implemented yet // TODO error states not implemented yet
break; break;
default: default:
log.warn("unhandled state " + tradeState); log.warn("unhandled state " + tradeProcessState);
break; break;
} }
} }

View file

@ -127,12 +127,6 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
// for irc demo // for irc demo
showAdvancedSettingsButton.setVisible(false); showAdvancedSettingsButton.setVisible(false);
showAdvancedSettingsButton.setManaged(false); showAdvancedSettingsButton.setManaged(false);
//TODO temp for testing
amountTextField.setText("1");
priceTextField.setText("1");
volumeTextField.setText("1");
} }
@Override @Override
@ -150,6 +144,11 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
priceColumn.setSortType((model.getDirection() == Direction.BUY) ? priceColumn.setSortType((model.getDirection() == Direction.BUY) ?
TableColumn.SortType.ASCENDING : TableColumn.SortType.DESCENDING); TableColumn.SortType.ASCENDING : TableColumn.SortType.DESCENDING);
table.sort(); table.sort();
//TODO temp for testing
amountTextField.setText("1");
priceTextField.setText("300");
volumeTextField.setText("300");
} }
@Override @Override

View file

@ -208,14 +208,14 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
isTakeOfferSpinnerVisible.set(true); isTakeOfferSpinnerVisible.set(true);
dataModel.takeOffer((trade) -> { dataModel.takeOffer((trade) -> {
trade.stateProperty().addListener((ov, oldValue, newValue) -> { trade.processStateProperty().addListener((ov, oldValue, newValue) -> {
log.debug("trade state = " + newValue); log.debug("trade state = " + newValue);
String msg = ""; String msg = "";
if (newValue.getErrorMessage() != null) if (newValue.getErrorMessage() != null)
msg = "\nError message: " + newValue.getErrorMessage(); msg = "\nError message: " + newValue.getErrorMessage();
switch (newValue) { switch (newValue) {
case OPEN: case INIT:
break; break;
case TAKE_OFFER_FEE_TX_CREATED: case TAKE_OFFER_FEE_TX_CREATED:
break; break;

View file

@ -18,6 +18,7 @@
package io.bitsquare.trade; package io.bitsquare.trade;
import io.bitsquare.offer.Offer; import io.bitsquare.offer.Offer;
import io.bitsquare.p2p.Peer;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction; import org.bitcoinj.core.Transaction;
@ -38,8 +39,16 @@ public class Trade implements Serializable {
// Enum // Enum
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public static enum State { public static enum LifeCycleState {
OPEN, OPEN_OFFER,
CANCELED,
PENDING,
COMPLETED,
FAILED
}
public static enum ProcessState {
INIT,
TAKE_OFFER_FEE_PUBLISH_FAILED, TAKE_OFFER_FEE_PUBLISH_FAILED,
TAKE_OFFER_FEE_TX_CREATED, TAKE_OFFER_FEE_TX_CREATED,
DEPOSIT_PUBLISHED, DEPOSIT_PUBLISHED,
@ -64,7 +73,7 @@ public class Trade implements Serializable {
private final Offer offer; private final Offer offer;
private final Date date; private final Date date;
private State state; private ProcessState processState;
private Coin tradeAmount; private Coin tradeAmount;
private Contract contract; private Contract contract;
private String contractAsJson; private String contractAsJson;
@ -72,13 +81,14 @@ public class Trade implements Serializable {
private String offererContractSignature; private String offererContractSignature;
private Transaction depositTx; private Transaction depositTx;
private Transaction payoutTx; private Transaction payoutTx;
private Peer tradingPeer;
// For changing values we use properties to get binding support in the UI (table) // For changing values we use properties to get binding support in the UI (table)
// When serialized those transient properties are not instantiated, so we instantiate them in the getters at first // When serialized those transient properties are not instantiated, so we instantiate them in the getters at first
// 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<State> _state; transient private ObjectProperty<ProcessState> _state;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -89,7 +99,7 @@ public class Trade implements Serializable {
this.offer = offer; this.offer = offer;
date = new Date(); date = new Date();
setState(State.OPEN); setProcessState(ProcessState.INIT);
} }
@ -97,6 +107,10 @@ public class Trade implements Serializable {
// Setters // Setters
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void setTradingPeer(Peer tradingPeer) {
this.tradingPeer = tradingPeer;
}
public void setTakerContractSignature(String takerSignature) { public void setTakerContractSignature(String takerSignature) {
this.takerContractSignature = takerSignature; this.takerContractSignature = takerSignature;
} }
@ -135,9 +149,9 @@ public class Trade implements Serializable {
this.payoutTx = tx; this.payoutTx = tx;
} }
public void setState(State state) { public void setProcessState(ProcessState processState) {
this.state = state; this.processState = processState;
stateProperty().set(state); processStateProperty().set(processState);
} }
@ -165,8 +179,8 @@ public class Trade implements Serializable {
return payoutTx; return payoutTx;
} }
public State getState() { public ProcessState getProcessState() {
return state; return processState;
} }
public Coin getSecurityDeposit() { public Coin getSecurityDeposit() {
@ -189,6 +203,11 @@ public class Trade implements Serializable {
return date; return date;
} }
public Peer getTradingPeer() {
return tradingPeer;
}
// When serialized those transient properties are not instantiated, so we need to instantiate them at first access // When serialized those transient properties are not instantiated, so we need to instantiate them at first access
public ObjectProperty<Coin> tradeAmountProperty() { public ObjectProperty<Coin> tradeAmountProperty() {
if (_tradeAmount == null) if (_tradeAmount == null)
@ -204,9 +223,9 @@ public class Trade implements Serializable {
return _tradeVolume; return _tradeVolume;
} }
public ObjectProperty<State> stateProperty() { public ObjectProperty<ProcessState> processStateProperty() {
if (_state == null) if (_state == null)
_state = new SimpleObjectProperty<>(state); _state = new SimpleObjectProperty<>(processState);
return _state; return _state;
} }
@ -216,7 +235,7 @@ public class Trade implements Serializable {
return "Trade{" + return "Trade{" +
"offer=" + offer + "offer=" + offer +
", date=" + date + ", date=" + date +
", state=" + state + ", state=" + processState +
", tradeAmount=" + tradeAmount + ", tradeAmount=" + tradeAmount +
", contract=" + contract + ", contract=" + contract +
", contractAsJson='" + contractAsJson + '\'' + ", contractAsJson='" + contractAsJson + '\'' +

View file

@ -90,7 +90,7 @@ 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, Offer> openOffers = FXCollections.observableHashMap(); private final ObservableMap<String, Trade> openOfferTrades = FXCollections.observableHashMap();
private final ObservableMap<String, Trade> pendingTrades = FXCollections.observableHashMap(); private final ObservableMap<String, Trade> pendingTrades = FXCollections.observableHashMap();
private final ObservableMap<String, Trade> closedTrades = FXCollections.observableHashMap(); private final ObservableMap<String, Trade> closedTrades = FXCollections.observableHashMap();
private final Map<String, MailboxMessage> mailboxMessages = new HashMap<>(); private final Map<String, MailboxMessage> mailboxMessages = new HashMap<>();
@ -119,9 +119,9 @@ public class TradeManager {
this.encryptionService = encryptionService; this.encryptionService = encryptionService;
this.offerBookService = offerBookService; this.offerBookService = offerBookService;
Serializable openOffersObject = persistence.read(this, "openOffers"); Serializable openOfferTradesObject = persistence.read(this, "openOfferTrades");
if (openOffersObject instanceof Map<?, ?>) { if (openOfferTradesObject instanceof Map<?, ?>) {
openOffers.putAll((Map<String, Offer>) openOffersObject); openOfferTrades.putAll((Map<String, Trade>) openOfferTradesObject);
} }
Serializable pendingTradesObject = persistence.read(this, "pendingTrades"); Serializable pendingTradesObject = persistence.read(this, "pendingTrades");
@ -143,33 +143,19 @@ 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, Offer> entry : openOffers.entrySet()) { for (Map.Entry<String, Trade> entry : openOfferTrades.entrySet()) {
createOffererAsBuyerProtocol(entry.getValue()); createOffererAsBuyerProtocol(entry.getValue());
} }
for (Map.Entry<String, Trade> entry : pendingTrades.entrySet()) { for (Map.Entry<String, Trade> entry : pendingTrades.entrySet()) {
// 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.
Trade trade = entry.getValue(); Trade trade = entry.getValue();
if (trade.getState() == Trade.State.FAULT) { if (isMyOffer(trade.getOffer())) {
closeTrade(trade); createOffererAsBuyerProtocol(trade);
} }
else { else {
Offer offer = trade.getOffer(); createTakerAsSellerProtocol(trade);
if (isMyOffer(offer)) {
createOffererAsBuyerProtocol(offer);
}
else {
CheckOfferAvailabilityModel model = new CheckOfferAvailabilityModel(offer, messageService, addressService);
CheckOfferAvailabilityProtocol protocol = new CheckOfferAvailabilityProtocol(model,
() -> {
disposeCheckOfferAvailabilityRequest(offer);
// TODO need to check that trade hijacking is not possible (taking trades form other peers)
if (offer.getState() == Offer.State.AVAILABLE || offer.getState() == Offer.State.RESERVED) {
createTakerAsSellerProtocol(trade, model.getPeer());
}
},
(errorMessage) -> disposeCheckOfferAvailabilityRequest(offer));
checkOfferAvailabilityProtocolMap.put(offer.getId(), protocol);
protocol.checkOfferAvailability();
}
} }
} }
@ -220,9 +206,10 @@ public class TradeManager {
PlaceOfferProtocol placeOfferProtocol = new PlaceOfferProtocol( PlaceOfferProtocol placeOfferProtocol = new PlaceOfferProtocol(
model, model,
(transaction) -> { (transaction) -> {
openOffers.put(offer.getId(), offer); Trade trade = new Trade(offer);
openOfferTrades.put(trade.getId(), trade);
persistOpenOffers(); persistOpenOffers();
createOffererAsBuyerProtocol(offer); createOffererAsBuyerProtocol(trade);
resultHandler.handleResult(transaction); resultHandler.handleResult(transaction);
}, },
(message) -> errorMessageHandler.handleErrorMessage(message) (message) -> errorMessageHandler.handleErrorMessage(message)
@ -340,8 +327,8 @@ public class TradeManager {
// Getters // Getters
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public ObservableMap<String, Offer> getOpenOffers() { public ObservableMap<String, Trade> getOpenOfferTrades() {
return openOffers; return openOfferTrades;
} }
public ObservableMap<String, Trade> getPendingTrades() { public ObservableMap<String, Trade> getPendingTrades() {
@ -372,8 +359,8 @@ public class TradeManager {
String offerId = offer.getId(); String offerId = offer.getId();
offerBookService.removeOffer(offer, offerBookService.removeOffer(offer,
() -> { () -> {
if (openOffers.containsKey(offerId)) { if (openOfferTrades.containsKey(offerId)) {
openOffers.remove(offerId); openOfferTrades.remove(offerId);
disposeCheckOfferAvailabilityRequest(offer); disposeCheckOfferAvailabilityRequest(offer);
persistOpenOffers(); persistOpenOffers();
if (removeFromBuyerAcceptsOfferProtocolMap && offererAsBuyerProtocolMap.containsKey(offerId)) { if (removeFromBuyerAcceptsOfferProtocolMap && offererAsBuyerProtocolMap.containsKey(offerId)) {
@ -413,8 +400,9 @@ public class TradeManager {
private Trade takeAvailableOffer(Coin amount, Offer offer, Peer peer) { private Trade takeAvailableOffer(Coin amount, Offer offer, Peer peer) {
Trade trade = createTrade(offer); Trade trade = createTrade(offer);
trade.setTradeAmount(amount); trade.setTradeAmount(amount);
trade.setTradingPeer(peer);
TakerAsSellerProtocol sellerTakesOfferProtocol = createTakerAsSellerProtocol(trade, peer); TakerAsSellerProtocol sellerTakesOfferProtocol = createTakerAsSellerProtocol(trade);
sellerTakesOfferProtocol.takeAvailableOffer(); sellerTakesOfferProtocol.takeAvailableOffer();
return trade; return trade;
} }
@ -432,11 +420,11 @@ public class TradeManager {
return trade; return trade;
} }
private TakerAsSellerProtocol createTakerAsSellerProtocol(Trade trade, Peer peer) { private TakerAsSellerProtocol createTakerAsSellerProtocol(Trade trade) {
trade.stateProperty().addListener((ov, oldValue, newValue) -> { trade.processStateProperty().addListener((ov, oldValue, newValue) -> {
log.debug("trade state = " + newValue); log.debug("trade state = " + newValue);
switch (newValue) { switch (newValue) {
case OPEN: case INIT:
break; break;
case TAKE_OFFER_FEE_TX_CREATED: case TAKE_OFFER_FEE_TX_CREATED:
case DEPOSIT_PUBLISHED: case DEPOSIT_PUBLISHED:
@ -458,7 +446,6 @@ public class TradeManager {
TakerAsSellerModel model = new TakerAsSellerModel( TakerAsSellerModel model = new TakerAsSellerModel(
trade, trade,
peer,
messageService, messageService,
mailboxService, mailboxService,
walletService, walletService,
@ -478,17 +465,7 @@ public class TradeManager {
} }
private void createOffererAsBuyerProtocol(Offer offer) { private void createOffererAsBuyerProtocol(Trade trade) {
Trade trade;
if (pendingTrades.containsKey(offer.getId())) {
trade = pendingTrades.get(offer.getId());
currentPendingTrade = trade;
}
else {
trade = new Trade(offer);
// don't save it in pendingTrades. It is only a potential trade
}
OffererAsBuyerModel model = new OffererAsBuyerModel( OffererAsBuyerModel model = new OffererAsBuyerModel(
trade, trade,
messageService, messageService,
@ -501,16 +478,16 @@ public class TradeManager {
// TODO check, remove listener // TODO check, remove listener
trade.stateProperty().addListener((ov, oldValue, newValue) -> { trade.processStateProperty().addListener((ov, oldValue, newValue) -> {
log.debug("trade state = " + newValue); log.debug("trade state = " + newValue);
switch (newValue) { switch (newValue) {
case OPEN: case INIT:
break; break;
case TAKE_OFFER_FEE_TX_CREATED: case TAKE_OFFER_FEE_TX_CREATED:
persistPendingTrades(); persistPendingTrades();
break; break;
case DEPOSIT_PUBLISHED: case DEPOSIT_PUBLISHED:
removeOpenOffer(offer, 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);
@ -531,7 +508,6 @@ public class TradeManager {
case MESSAGE_SENDING_FAILED: case MESSAGE_SENDING_FAILED:
case FAULT: case FAULT:
closeTrade(trade); closeTrade(trade);
offererAsBuyerProtocolMap.get(trade.getId()).cleanup();
break; break;
default: default:
log.warn("Unhandled trade state: " + newValue); log.warn("Unhandled trade state: " + newValue);
@ -540,7 +516,7 @@ public class TradeManager {
}); });
OffererAsBuyerProtocol protocol = new OffererAsBuyerProtocol(model); OffererAsBuyerProtocol protocol = new OffererAsBuyerProtocol(model);
offererAsBuyerProtocolMap.put(offer.getId(), protocol); offererAsBuyerProtocolMap.put(trade.getId(), protocol);
if (mailboxMessages.containsKey(trade.getId())) { if (mailboxMessages.containsKey(trade.getId())) {
log.debug("OffererAsBuyerProtocol setMailboxMessage " + trade.getId()); log.debug("OffererAsBuyerProtocol setMailboxMessage " + trade.getId());
protocol.setMailboxMessage(mailboxMessages.get(trade.getId())); protocol.setMailboxMessage(mailboxMessages.get(trade.getId()));
@ -612,13 +588,13 @@ public class TradeManager {
} }
boolean isOfferOpen(String offerId) { boolean isOfferOpen(String offerId) {
return openOffers.containsKey(offerId) return openOfferTrades.containsKey(offerId)
&& (openOffers.get(offerId).getState() == Offer.State.UNKNOWN && (openOfferTrades.get(offerId).getOffer().getState() == Offer.State.UNKNOWN
|| openOffers.get(offerId).getState() == Offer.State.AVAILABLE); || openOfferTrades.get(offerId).getOffer().getState() == Offer.State.AVAILABLE);
} }
private void persistOpenOffers() { private void persistOpenOffers() {
persistence.write(this, "openOffers", (Map<String, Offer>) new HashMap<>(openOffers)); persistence.write(this, "openOfferTrades", (Map<String, Trade>) new HashMap<>(openOfferTrades));
} }
private void persistPendingTrades() { private void persistPendingTrades() {

View file

@ -22,7 +22,6 @@ 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.trade.Trade;
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;
@ -42,8 +41,6 @@ import io.bitsquare.trade.protocol.trade.offerer.tasks.VerifyAndSignContract;
import io.bitsquare.trade.protocol.trade.offerer.tasks.VerifyTakeOfferFeePayment; import io.bitsquare.trade.protocol.trade.offerer.tasks.VerifyTakeOfferFeePayment;
import io.bitsquare.trade.protocol.trade.offerer.tasks.VerifyTakerAccount; import io.bitsquare.trade.protocol.trade.offerer.tasks.VerifyTakerAccount;
import org.bitcoinj.core.TransactionConfidence;
import javafx.application.Platform; import javafx.application.Platform;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -57,8 +54,6 @@ public class OffererAsBuyerProtocol {
private final OffererAsBuyerModel model; private final OffererAsBuyerModel model;
private final MessageHandler messageHandler; private final MessageHandler messageHandler;
private TransactionConfidence.Listener transactionConfidenceListener;
private TransactionConfidence transactionConfidence;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Constructor // Constructor
@ -94,11 +89,6 @@ public class OffererAsBuyerProtocol {
// tradeMessageService and transactionConfidence use CopyOnWriteArrayList as listeners, but be safe and delay remove a bit. // tradeMessageService and transactionConfidence use CopyOnWriteArrayList as listeners, but be safe and delay remove a bit.
Platform.runLater(() -> { Platform.runLater(() -> {
model.messageService.removeMessageHandler(messageHandler); model.messageService.removeMessageHandler(messageHandler);
if (transactionConfidence != null) {
if (!transactionConfidence.removeEventListener(transactionConfidenceListener))
throw new RuntimeException("Remove transactionConfidenceListener failed at BuyerAsOffererProtocol.");
}
}); });
} }
@ -109,16 +99,11 @@ public class OffererAsBuyerProtocol {
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.taker.peer = 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("sequence at handleTakeOfferFeePayedMessage completed"); (errorMessage) -> handleTaskRunnerFault(errorMessage));
},
(errorMessage) -> {
log.error(errorMessage);
}
);
taskRunner.addTasks( taskRunner.addTasks(
ProcessRequestDepositTxInputsMessage.class, ProcessRequestDepositTxInputsMessage.class,
CreateOffererDepositTxInputs.class, CreateOffererDepositTxInputs.class,
@ -131,23 +116,8 @@ public class OffererAsBuyerProtocol {
model.setTradeMessage(tradeMessage); model.setTradeMessage(tradeMessage);
TaskRunner<OffererAsBuyerModel> taskRunner = new TaskRunner<>(model, TaskRunner<OffererAsBuyerModel> taskRunner = new TaskRunner<>(model,
() -> { () -> log.debug("taskRunner at handleRequestOffererPublishDepositTxMessage completed"),
log.debug("taskRunner at handleRequestOffererPublishDepositTxMessage completed"); (errorMessage) -> handleTaskRunnerFault(errorMessage));
transactionConfidenceListener = (tx, reason) -> {
log.trace("onConfidenceChanged " + tx.getConfidence());
if (reason == TransactionConfidence.Listener.ChangeReason.TYPE && tx.getConfidence().getConfidenceType() == TransactionConfidence
.ConfidenceType.BUILDING) {
model.trade.setState(Trade.State.DEPOSIT_CONFIRMED);
}
};
transactionConfidence = model.trade.getDepositTx().getConfidence();
transactionConfidence.addEventListener(transactionConfidenceListener);
},
(errorMessage) -> {
log.error(errorMessage);
}
);
taskRunner.addTasks( taskRunner.addTasks(
ProcessRequestOffererPublishDepositTxMessage.class, ProcessRequestOffererPublishDepositTxMessage.class,
VerifyTakerAccount.class, VerifyTakerAccount.class,
@ -167,13 +137,8 @@ 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("sequence at handleBankTransferStartedUIEvent completed"); (errorMessage) -> handleTaskRunnerFault(errorMessage));
},
(errorMessage) -> {
log.error(errorMessage);
}
);
taskRunner.addTasks( taskRunner.addTasks(
CreateAndSignPayoutTx.class, CreateAndSignPayoutTx.class,
VerifyTakeOfferFeePayment.class, VerifyTakeOfferFeePayment.class,
@ -193,14 +158,11 @@ public class OffererAsBuyerProtocol {
TaskRunner<OffererAsBuyerModel> taskRunner = new TaskRunner<>(model, TaskRunner<OffererAsBuyerModel> taskRunner = new TaskRunner<>(model,
() -> { () -> {
log.debug("sequence at handlePayoutTxPublishedMessage completed"); log.debug("sequence at handlePayoutTxPublishedMessage completed");
// we are done! // we are done!
model.onComplete(); model.onComplete();
}, },
(errorMessage) -> { (errorMessage) -> handleTaskRunnerFault(errorMessage));
log.error(errorMessage);
}
);
taskRunner.addTasks(ProcessPayoutTxPublishedMessage.class); taskRunner.addTasks(ProcessPayoutTxPublishedMessage.class);
taskRunner.run(); taskRunner.run();
} }
@ -233,4 +195,8 @@ public class OffererAsBuyerProtocol {
} }
} }
} }
private void handleTaskRunnerFault(String errorMessage) {
cleanup();
}
} }

View file

@ -18,7 +18,6 @@
package io.bitsquare.trade.protocol.trade.offerer.models; package io.bitsquare.trade.protocol.trade.offerer.models;
import io.bitsquare.fiat.FiatAccount; import io.bitsquare.fiat.FiatAccount;
import io.bitsquare.p2p.Peer;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction; import org.bitcoinj.core.Transaction;
@ -34,7 +33,6 @@ public class Taker implements Serializable {
private static final long serialVersionUID = 2660909397210346486L; private static final long serialVersionUID = 2660909397210346486L;
// written by tasks // written by tasks
public Peer peer;
public String accountId; public String accountId;
public FiatAccount fiatAccount; public FiatAccount fiatAccount;
public PublicKey p2pSigPublicKey; public PublicKey p2pSigPublicKey;

View file

@ -43,7 +43,7 @@ public class ProcessPayoutTxPublishedMessage extends Task<OffererAsBuyerModel> {
model.trade.setPayoutTx(checkNotNull(((PayoutTxPublishedMessage) model.getTradeMessage()).payoutTx)); model.trade.setPayoutTx(checkNotNull(((PayoutTxPublishedMessage) model.getTradeMessage()).payoutTx));
model.trade.setState(Trade.State.PAYOUT_PUBLISHED); model.trade.setProcessState(Trade.ProcessState.PAYOUT_PUBLISHED);
complete(); complete();
} catch (Throwable t) { } catch (Throwable t) {

View file

@ -45,7 +45,7 @@ public class RequestTakerDepositPayment extends Task<OffererAsBuyerModel> {
model.offerer.fiatAccount, model.offerer.fiatAccount,
model.offerer.accountId); model.offerer.accountId);
model.messageService.sendMessage(model.taker.peer, tradeMessage, new SendMessageListener() { model.messageService.sendMessage(model.trade.getTradingPeer(), tradeMessage, new SendMessageListener() {
@Override @Override
public void handleResult() { public void handleResult() {
log.trace("RequestTakerDepositPaymentMessage successfully arrived at peer"); log.trace("RequestTakerDepositPaymentMessage successfully arrived at peer");

View file

@ -43,26 +43,26 @@ public class SendBankTransferStartedMessage extends Task<OffererAsBuyerModel> {
model.taker.payoutAmount, model.taker.payoutAmount,
model.offerer.addressEntry.getAddressString()); model.offerer.addressEntry.getAddressString());
model.messageService.sendMessage(model.taker.peer, tradeMessage, model.messageService.sendMessage(model.trade.getTradingPeer(), tradeMessage,
model.taker.p2pSigPublicKey, model.taker.p2pSigPublicKey,
model.taker.p2pEncryptPubKey, model.taker.p2pEncryptPubKey,
new SendMessageListener() { new SendMessageListener() {
@Override @Override
public void handleResult() { public void handleResult() {
log.trace("Sending BankTransferInitedMessage succeeded."); log.trace("Sending BankTransferInitedMessage succeeded.");
model.trade.setState(Trade.State.FIAT_PAYMENT_STARTED); model.trade.setProcessState(Trade.ProcessState.FIAT_PAYMENT_STARTED);
complete(); complete();
} }
@Override @Override
public void handleFault() { public void handleFault() {
failed("Sending BankTransferInitedMessage failed."); failed("Sending BankTransferInitedMessage failed.");
model.trade.setState(Trade.State.FAULT); model.trade.setProcessState(Trade.ProcessState.FAULT);
} }
}); });
} catch (Throwable t) { } catch (Throwable t) {
failed("Sending BankTransferInitedMessage failed."); failed("Sending BankTransferInitedMessage failed.");
model.trade.setState(Trade.State.FAULT); model.trade.setProcessState(Trade.ProcessState.FAULT);
} }
} }
} }

View file

@ -37,7 +37,7 @@ public class SendDepositTxToTaker extends Task<OffererAsBuyerModel> {
protected void doRun() { protected void doRun() {
DepositTxPublishedMessage tradeMessage = new DepositTxPublishedMessage(model.id, model.trade.getDepositTx()); DepositTxPublishedMessage tradeMessage = new DepositTxPublishedMessage(model.id, model.trade.getDepositTx());
model.messageService.sendMessage(model.taker.peer, tradeMessage, new SendMessageListener() { model.messageService.sendMessage(model.trade.getTradingPeer(), tradeMessage, new SendMessageListener() {
@Override @Override
public void handleResult() { public void handleResult() {
log.trace("DepositTxPublishedMessage successfully arrived at peer"); log.trace("DepositTxPublishedMessage successfully arrived at peer");

View file

@ -48,7 +48,7 @@ public class SetupListenerForBlockChainConfirmation extends Task<OffererAsBuyerM
public void onConfidenceChanged(Transaction tx, ChangeReason reason) { public void onConfidenceChanged(Transaction tx, ChangeReason reason) {
log.trace("onConfidenceChanged " + tx.getConfidence()); log.trace("onConfidenceChanged " + tx.getConfidence());
if (reason == ChangeReason.TYPE && tx.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING) { if (reason == ChangeReason.TYPE && tx.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING) {
model.trade.setState(Trade.State.DEPOSIT_CONFIRMED); model.trade.setProcessState(Trade.ProcessState.DEPOSIT_CONFIRMED);
// transactionConfidence use CopyOnWriteArrayList as listeners, but be safe and delay remove a bit. // transactionConfidence use CopyOnWriteArrayList as listeners, but be safe and delay remove a bit.
Platform.runLater(() -> removeEventListener()); Platform.runLater(() -> removeEventListener());
@ -62,6 +62,6 @@ public class SetupListenerForBlockChainConfirmation extends Task<OffererAsBuyerM
private void removeEventListener() { private void removeEventListener() {
if (!transactionConfidence.removeEventListener(transactionConfidenceListener)) if (!transactionConfidence.removeEventListener(transactionConfidenceListener))
throw new RuntimeException("Remove transactionConfidenceListener failed at SetupListenerForBlockChainConfirmation."); log.error("Remove transactionConfidenceListener failed at SetupListenerForBlockChainConfirmation.");
} }
} }

View file

@ -59,7 +59,7 @@ public class SignAndPublishDepositTx extends Task<OffererAsBuyerModel> {
log.trace("offererSignAndPublishTx succeeded " + transaction); log.trace("offererSignAndPublishTx succeeded " + transaction);
model.trade.setDepositTx(transaction); model.trade.setDepositTx(transaction);
model.trade.setState(Trade.State.DEPOSIT_PUBLISHED); model.trade.setProcessState(Trade.ProcessState.DEPOSIT_PUBLISHED);
complete(); complete();
} }

View file

@ -27,7 +27,7 @@ import io.bitsquare.trade.protocol.trade.messages.DepositTxPublishedMessage;
import io.bitsquare.trade.protocol.trade.messages.FiatTransferStartedMessage; import io.bitsquare.trade.protocol.trade.messages.FiatTransferStartedMessage;
import io.bitsquare.trade.protocol.trade.messages.RequestTakerDepositPaymentMessage; import io.bitsquare.trade.protocol.trade.messages.RequestTakerDepositPaymentMessage;
import io.bitsquare.trade.protocol.trade.messages.TradeMessage; import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
import io.bitsquare.trade.protocol.trade.offerer.tasks.SetupListenerForBlockChainConfirmation; import io.bitsquare.trade.protocol.trade.taker.tasks.SetupListenerForBlockChainConfirmation;
import io.bitsquare.trade.protocol.trade.taker.models.TakerAsSellerModel; import io.bitsquare.trade.protocol.trade.taker.models.TakerAsSellerModel;
import io.bitsquare.trade.protocol.trade.taker.tasks.BroadcastTakeOfferFeeTx; import io.bitsquare.trade.protocol.trade.taker.tasks.BroadcastTakeOfferFeeTx;
import io.bitsquare.trade.protocol.trade.taker.tasks.CreateAndSignContract; import io.bitsquare.trade.protocol.trade.taker.tasks.CreateAndSignContract;
@ -43,11 +43,6 @@ import io.bitsquare.trade.protocol.trade.taker.tasks.TakerCommitDepositTx;
import io.bitsquare.trade.protocol.trade.taker.tasks.TakerCreatesAndSignsDepositTx; import io.bitsquare.trade.protocol.trade.taker.tasks.TakerCreatesAndSignsDepositTx;
import io.bitsquare.trade.protocol.trade.taker.tasks.VerifyOfferFeePayment; import io.bitsquare.trade.protocol.trade.taker.tasks.VerifyOfferFeePayment;
import io.bitsquare.trade.protocol.trade.taker.tasks.VerifyOffererAccount; import io.bitsquare.trade.protocol.trade.taker.tasks.VerifyOffererAccount;
import io.bitsquare.util.Utilities;
import java.util.function.Function;
import javafx.animation.AnimationTimer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -56,11 +51,9 @@ import static io.bitsquare.util.Validator.nonEmptyStringOf;
public class TakerAsSellerProtocol { public class TakerAsSellerProtocol {
private static final Logger log = LoggerFactory.getLogger(TakerAsSellerProtocol.class); private static final Logger log = LoggerFactory.getLogger(TakerAsSellerProtocol.class);
private static final int TIMEOUT_DELAY = 10000;
private final TakerAsSellerModel model; private final TakerAsSellerModel model;
private final MessageHandler messageHandler; private final MessageHandler messageHandler;
private AnimationTimer timeoutTimer;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -72,8 +65,6 @@ public class TakerAsSellerProtocol {
this.model = model; this.model = model;
messageHandler = this::handleMessage; messageHandler = this::handleMessage;
model.messageService.addMessageHandler(messageHandler); model.messageService.addMessageHandler(messageHandler);
} }
@ -105,10 +96,8 @@ public class TakerAsSellerProtocol {
() -> { () -> {
log.debug("taskRunner at takeAvailableOffer completed"); log.debug("taskRunner at takeAvailableOffer completed");
}, },
(errorMessage) -> { (errorMessage) -> handleTaskRunnerFault(errorMessage));
log.error(errorMessage);
}
);
taskRunner.addTasks( taskRunner.addTasks(
CreateTakeOfferFeeTx.class, CreateTakeOfferFeeTx.class,
BroadcastTakeOfferFeeTx.class, BroadcastTakeOfferFeeTx.class,
@ -129,10 +118,8 @@ public class TakerAsSellerProtocol {
() -> { () -> {
log.debug("taskRunner at handleTakerDepositPaymentRequestMessage completed"); log.debug("taskRunner at handleTakerDepositPaymentRequestMessage completed");
}, },
(errorMessage) -> { (errorMessage) -> handleTaskRunnerFault(errorMessage));
log.error(errorMessage);
}
);
taskRunner.addTasks( taskRunner.addTasks(
ProcessRequestTakerDepositPaymentMessage.class, ProcessRequestTakerDepositPaymentMessage.class,
VerifyOffererAccount.class, VerifyOffererAccount.class,
@ -150,10 +137,8 @@ public class TakerAsSellerProtocol {
() -> { () -> {
log.debug("taskRunner at handleDepositTxPublishedMessage completed"); log.debug("taskRunner at handleDepositTxPublishedMessage completed");
}, },
(errorMessage) -> { (errorMessage) -> handleTaskRunnerFault(errorMessage));
log.error(errorMessage);
}
);
taskRunner.addTasks( taskRunner.addTasks(
ProcessDepositTxPublishedMessage.class, ProcessDepositTxPublishedMessage.class,
TakerCommitDepositTx.class, TakerCommitDepositTx.class,
@ -169,10 +154,8 @@ public class TakerAsSellerProtocol {
() -> { () -> {
log.debug("taskRunner at handleFiatTransferStartedMessage completed"); log.debug("taskRunner at handleFiatTransferStartedMessage completed");
}, },
(errorMessage) -> { (errorMessage) -> handleTaskRunnerFault(errorMessage));
log.error(errorMessage);
}
);
taskRunner.addTasks(ProcessFiatTransferStartedMessage.class); taskRunner.addTasks(ProcessFiatTransferStartedMessage.class);
taskRunner.run(); taskRunner.run();
} }
@ -184,7 +167,7 @@ public class TakerAsSellerProtocol {
// User clicked the "bank transfer received" button, so we release the funds for pay out // User clicked the "bank transfer received" button, so we release the funds for pay out
public void onFiatPaymentReceived() { public void onFiatPaymentReceived() {
model.trade.setState(Trade.State.FIAT_PAYMENT_RECEIVED); model.trade.setProcessState(Trade.ProcessState.FIAT_PAYMENT_RECEIVED);
TaskRunner<TakerAsSellerModel> taskRunner = new TaskRunner<>(model, TaskRunner<TakerAsSellerModel> taskRunner = new TaskRunner<>(model,
() -> { () -> {
@ -193,10 +176,8 @@ public class TakerAsSellerProtocol {
// we are done! // we are done!
model.onComplete(); model.onComplete();
}, },
(errorMessage) -> { (errorMessage) -> handleTaskRunnerFault(errorMessage));
log.error(errorMessage);
}
);
taskRunner.addTasks( taskRunner.addTasks(
SignAndPublishPayoutTx.class, SignAndPublishPayoutTx.class,
VerifyOfferFeePayment.class, VerifyOfferFeePayment.class,
@ -232,22 +213,8 @@ public class TakerAsSellerProtocol {
} }
} }
private void handleTaskRunnerFault(String errorMessage) {
/////////////////////////////////////////////////////////////////////////////////////////// cleanup();
// Timeout
///////////////////////////////////////////////////////////////////////////////////////////
private void startTimeout(Function<AnimationTimer, Void> callback) {
stopTimeout();
timeoutTimer = Utilities.setTimeout(TIMEOUT_DELAY, callback);
timeoutTimer.start();
}
private void stopTimeout() {
if (timeoutTimer != null) {
timeoutTimer.stop();
timeoutTimer = null;
}
} }
} }

View file

@ -18,7 +18,6 @@
package io.bitsquare.trade.protocol.trade.taker.models; package io.bitsquare.trade.protocol.trade.taker.models;
import io.bitsquare.fiat.FiatAccount; import io.bitsquare.fiat.FiatAccount;
import io.bitsquare.p2p.Peer;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.bitcoinj.core.TransactionOutput; import org.bitcoinj.core.TransactionOutput;
@ -32,10 +31,6 @@ import java.util.List;
public class Offerer implements Serializable { public class Offerer implements Serializable {
private static final long serialVersionUID = 1582902150121576205L; private static final long serialVersionUID = 1582902150121576205L;
// Those fields are set at constructor but not declared as final because constructor is not called in case model gets created from a persisted model
// Declared transient as they will be provided in any case at construction time
public Peer peer;
// written by tasks // written by tasks
public byte[] tradeWalletPubKey; public byte[] tradeWalletPubKey;
public Coin payoutAmount; public Coin payoutAmount;

View file

@ -22,7 +22,6 @@ import io.bitsquare.btc.WalletService;
import io.bitsquare.crypto.SignatureService; import io.bitsquare.crypto.SignatureService;
import io.bitsquare.p2p.MailboxService; import io.bitsquare.p2p.MailboxService;
import io.bitsquare.p2p.MessageService; import io.bitsquare.p2p.MessageService;
import io.bitsquare.p2p.Peer;
import io.bitsquare.persistence.Persistence; import io.bitsquare.persistence.Persistence;
import io.bitsquare.trade.Trade; import io.bitsquare.trade.Trade;
import io.bitsquare.trade.protocol.trade.SharedTradeModel; import io.bitsquare.trade.protocol.trade.SharedTradeModel;
@ -48,7 +47,6 @@ public class TakerAsSellerModel extends SharedTradeModel implements Serializable
private Transaction payoutTx; private Transaction payoutTx;
public TakerAsSellerModel(Trade trade, public TakerAsSellerModel(Trade trade,
Peer offererPeer,
MessageService messageService, MessageService messageService,
MailboxService mailboxService, MailboxService mailboxService,
WalletService walletService, WalletService walletService,
@ -82,8 +80,6 @@ public class TakerAsSellerModel extends SharedTradeModel implements Serializable
offerer = new Offerer(); offerer = new Offerer();
} }
offerer.peer = offererPeer;
taker.registrationPubKey = walletService.getRegistrationAddressEntry().getPubKey(); taker.registrationPubKey = walletService.getRegistrationAddressEntry().getPubKey();
taker.registrationKeyPair = walletService.getRegistrationAddressEntry().getKeyPair(); taker.registrationKeyPair = walletService.getRegistrationAddressEntry().getKeyPair();
taker.addressEntry = walletService.getAddressEntry(id); taker.addressEntry = walletService.getAddressEntry(id);

View file

@ -47,14 +47,14 @@ public class BroadcastTakeOfferFeeTx extends Task<TakerAsSellerModel> {
public void onSuccess(Transaction transaction) { public void onSuccess(Transaction transaction) {
log.debug("Take offer fee published successfully. Transaction ID = " + transaction.getHashAsString()); log.debug("Take offer fee published successfully. Transaction ID = " + transaction.getHashAsString());
model.trade.setState(Trade.State.TAKE_OFFER_FEE_PUBLISHED); model.trade.setProcessState(Trade.ProcessState.TAKE_OFFER_FEE_PUBLISHED);
complete(); complete();
} }
@Override @Override
public void onFailure(@NotNull Throwable t) { public void onFailure(@NotNull Throwable t) {
model.trade.setState(Trade.State.TAKE_OFFER_FEE_PUBLISH_FAILED); model.trade.setProcessState(Trade.ProcessState.TAKE_OFFER_FEE_PUBLISH_FAILED);
failed(t); failed(t);
} }
@ -63,7 +63,7 @@ public class BroadcastTakeOfferFeeTx extends Task<TakerAsSellerModel> {
appendToErrorMessage("Take offer fee payment failed. Maybe your network connection was lost. Please try again."); appendToErrorMessage("Take offer fee payment failed. Maybe your network connection was lost. Please try again.");
appendToErrorMessage(e.getMessage()); appendToErrorMessage(e.getMessage());
model.trade.setState(Trade.State.FAULT); model.trade.setProcessState(Trade.ProcessState.FAULT);
failed(e); failed(e);
} }

View file

@ -40,13 +40,13 @@ public class CreateTakeOfferFeeTx extends Task<TakerAsSellerModel> {
Transaction createTakeOfferFeeTx = model.tradeWalletService.createTakeOfferFeeTx(model.taker.addressEntry); Transaction createTakeOfferFeeTx = model.tradeWalletService.createTakeOfferFeeTx(model.taker.addressEntry);
model.setTakeOfferFeeTx(createTakeOfferFeeTx); model.setTakeOfferFeeTx(createTakeOfferFeeTx);
model.trade.setState(Trade.State.TAKE_OFFER_FEE_TX_CREATED); model.trade.setProcessState(Trade.ProcessState.TAKE_OFFER_FEE_TX_CREATED);
complete(); complete();
} catch (Exception e) { } catch (Exception e) {
appendToErrorMessage(e.getMessage()); appendToErrorMessage(e.getMessage());
model.trade.setState(Trade.State.FAULT); model.trade.setProcessState(Trade.ProcessState.FAULT);
failed(e); failed(e);
} }

View file

@ -43,7 +43,7 @@ public class ProcessDepositTxPublishedMessage extends Task<TakerAsSellerModel> {
DepositTxPublishedMessage message = (DepositTxPublishedMessage) model.getTradeMessage(); DepositTxPublishedMessage message = (DepositTxPublishedMessage) model.getTradeMessage();
model.trade.setDepositTx(checkNotNull(message.depositTx)); model.trade.setDepositTx(checkNotNull(message.depositTx));
model.trade.setState(Trade.State.DEPOSIT_PUBLISHED); model.trade.setProcessState(Trade.ProcessState.DEPOSIT_PUBLISHED);
complete(); complete();
} catch (Throwable t) { } catch (Throwable t) {

View file

@ -46,11 +46,11 @@ public class ProcessFiatTransferStartedMessage extends Task<TakerAsSellerModel>
model.offerer.payoutAmount = positiveCoinOf(nonZeroCoinOf(message.offererPayoutAmount)); model.offerer.payoutAmount = positiveCoinOf(nonZeroCoinOf(message.offererPayoutAmount));
model.taker.payoutAmount = positiveCoinOf(nonZeroCoinOf(message.takerPayoutAmount)); model.taker.payoutAmount = positiveCoinOf(nonZeroCoinOf(message.takerPayoutAmount));
model.offerer.payoutAddressString = nonEmptyStringOf(message.offererPayoutAddress); model.offerer.payoutAddressString = nonEmptyStringOf(message.offererPayoutAddress);
model.trade.setState(Trade.State.FIAT_PAYMENT_STARTED); model.trade.setProcessState(Trade.ProcessState.FIAT_PAYMENT_STARTED);
complete(); complete();
} catch (Throwable t) { } catch (Throwable t) {
model.trade.setState(Trade.State.FAULT); model.trade.setProcessState(Trade.ProcessState.FAULT);
failed(t); failed(t);
} }
} }

View file

@ -36,7 +36,7 @@ public class SendPayoutTxToOfferer extends Task<TakerAsSellerModel> {
@Override @Override
protected void doRun() { protected void doRun() {
PayoutTxPublishedMessage tradeMessage = new PayoutTxPublishedMessage(model.id, model.getPayoutTx()); PayoutTxPublishedMessage tradeMessage = new PayoutTxPublishedMessage(model.id, model.getPayoutTx());
model.messageService.sendMessage(model.offerer.peer, model.messageService.sendMessage(model.trade.getTradingPeer(),
tradeMessage, tradeMessage,
model.offerer.p2pSigPublicKey, model.offerer.p2pSigPublicKey,
model.offerer.p2pEncryptPubKey, model.offerer.p2pEncryptPubKey,

View file

@ -47,7 +47,7 @@ public class SendRequestDepositTxInputsMessage extends Task<TakerAsSellerModel>
model.taker.tradeWalletPubKey model.taker.tradeWalletPubKey
); );
model.messageService.sendMessage(model.offerer.peer, msg, new SendMessageListener() { model.messageService.sendMessage(model.trade.getTradingPeer(), msg, new SendMessageListener() {
@Override @Override
public void handleResult() { public void handleResult() {
log.trace("Sending TakeOfferFeePayedMessage succeeded."); log.trace("Sending TakeOfferFeePayedMessage succeeded.");
@ -67,7 +67,7 @@ public class SendRequestDepositTxInputsMessage extends Task<TakerAsSellerModel>
"connection. " + "connection. " +
"We persisted the state of the trade, please try again later or cancel that trade."); "We persisted the state of the trade, please try again later or cancel that trade.");
model.trade.setState(Trade.State.MESSAGE_SENDING_FAILED); model.trade.setProcessState(Trade.ProcessState.MESSAGE_SENDING_FAILED);
failed(); failed();
} }

View file

@ -49,7 +49,7 @@ public class SendSignedTakerDepositTx extends Task<TakerAsSellerModel> {
model.taker.outputs model.taker.outputs
); );
model.messageService.sendMessage(model.offerer.peer, tradeMessage, new SendMessageListener() { model.messageService.sendMessage(model.trade.getTradingPeer(), tradeMessage, new SendMessageListener() {
@Override @Override
public void handleResult() { public void handleResult() {
complete(); complete();

View file

@ -20,7 +20,7 @@ package io.bitsquare.trade.protocol.trade.taker.tasks;
import io.bitsquare.common.taskrunner.Task; import io.bitsquare.common.taskrunner.Task;
import io.bitsquare.common.taskrunner.TaskRunner; import io.bitsquare.common.taskrunner.TaskRunner;
import io.bitsquare.trade.Trade; import io.bitsquare.trade.Trade;
import io.bitsquare.trade.protocol.trade.offerer.models.OffererAsBuyerModel; import io.bitsquare.trade.protocol.trade.taker.models.TakerAsSellerModel;
import org.bitcoinj.core.Transaction; import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionConfidence; import org.bitcoinj.core.TransactionConfidence;
@ -30,25 +30,26 @@ import javafx.application.Platform;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public class SetupListenerForBlockChainConfirmation extends Task<OffererAsBuyerModel> { public class SetupListenerForBlockChainConfirmation extends Task<TakerAsSellerModel> {
private static final Logger log = LoggerFactory.getLogger(SetupListenerForBlockChainConfirmation.class); private static final Logger log = LoggerFactory.getLogger(SetupListenerForBlockChainConfirmation.class);
private TransactionConfidence.Listener transactionConfidenceListener; private TransactionConfidence.Listener transactionConfidenceListener;
private TransactionConfidence transactionConfidence; private TransactionConfidence transactionConfidence;
public SetupListenerForBlockChainConfirmation(TaskRunner taskHandler, OffererAsBuyerModel model) { public SetupListenerForBlockChainConfirmation(TaskRunner taskHandler, TakerAsSellerModel model) {
super(taskHandler, model); super(taskHandler, model);
} }
@Override @Override
protected void doRun() { protected void doRun() {
try {
transactionConfidence = model.trade.getDepositTx().getConfidence(); transactionConfidence = model.trade.getDepositTx().getConfidence();
transactionConfidenceListener = new TransactionConfidence.Listener() { transactionConfidenceListener = new TransactionConfidence.Listener() {
@Override @Override
public void onConfidenceChanged(Transaction tx, ChangeReason reason) { public void onConfidenceChanged(Transaction tx, ChangeReason reason) {
log.trace("onConfidenceChanged " + tx.getConfidence()); log.trace("onConfidenceChanged " + tx.getConfidence());
if (reason == ChangeReason.TYPE && tx.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING) { if (reason == ChangeReason.TYPE && tx.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING) {
model.trade.setState(Trade.State.DEPOSIT_CONFIRMED); model.trade.setProcessState(Trade.ProcessState.DEPOSIT_CONFIRMED);
// transactionConfidence use CopyOnWriteArrayList as listeners, but be safe and delay remove a bit. // transactionConfidence use CopyOnWriteArrayList as listeners, but be safe and delay remove a bit.
Platform.runLater(() -> removeEventListener()); Platform.runLater(() -> removeEventListener());
@ -58,10 +59,14 @@ public class SetupListenerForBlockChainConfirmation extends Task<OffererAsBuyerM
transactionConfidence.addEventListener(transactionConfidenceListener); transactionConfidence.addEventListener(transactionConfidenceListener);
complete(); complete();
} catch (Throwable t) {
t.printStackTrace();
log.error(t.getMessage());
}
} }
private void removeEventListener() { private void removeEventListener() {
if (!transactionConfidence.removeEventListener(transactionConfidenceListener)) if (!transactionConfidence.removeEventListener(transactionConfidenceListener))
throw new RuntimeException("Remove transactionConfidenceListener failed at SetupListenerForBlockChainConfirmation."); log.error("Remove transactionConfidenceListener failed at SetupListenerForBlockChainConfirmation.");
} }
} }

View file

@ -55,7 +55,7 @@ public class SignAndPublishPayoutTx extends Task<TakerAsSellerModel> {
@Override @Override
public void onSuccess(Transaction transaction) { public void onSuccess(Transaction transaction) {
model.setPayoutTx(transaction); model.setPayoutTx(transaction);
model.trade.setState(Trade.State.PAYOUT_PUBLISHED); model.trade.setProcessState(Trade.ProcessState.PAYOUT_PUBLISHED);
complete(); complete();
} }

View file

@ -21,6 +21,8 @@ import io.bitsquare.common.taskrunner.Task;
import io.bitsquare.common.taskrunner.TaskRunner; 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.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -34,7 +36,9 @@ public class TakerCommitDepositTx extends Task<TakerAsSellerModel> {
@Override @Override
protected void doRun() { protected void doRun() {
try { try {
model.tradeWalletService.takerCommitsDepositTx(model.trade.getDepositTx()); // We need to put the tx into our wallet to have a fully setup tx
Transaction localDepositTx = model.tradeWalletService.takerCommitsDepositTx(model.trade.getDepositTx());
model.trade.setDepositTx(localDepositTx);
complete(); complete();
} catch (Throwable t) { } catch (Throwable t) {

View file

@ -59,9 +59,9 @@ public class TakerCreatesAndSignsDepositTx extends Task<TakerAsSellerModel> {
complete(); complete();
} catch (Exception e) { } catch (Exception e) {
Trade.State state = Trade.State.FAULT; Trade.ProcessState processState = Trade.ProcessState.FAULT;
state.setErrorMessage(errorMessage); processState.setErrorMessage(errorMessage);
model.trade.setState(state); model.trade.setProcessState(processState);
failed(e); failed(e);
} }