diff --git a/core/src/main/java/bisq/core/offer/placeoffer/PlaceOfferProtocol.java b/core/src/main/java/bisq/core/offer/placeoffer/PlaceOfferProtocol.java index aafa8bff1a..55a005bed8 100644 --- a/core/src/main/java/bisq/core/offer/placeoffer/PlaceOfferProtocol.java +++ b/core/src/main/java/bisq/core/offer/placeoffer/PlaceOfferProtocol.java @@ -17,6 +17,7 @@ package bisq.core.offer.placeoffer; +import bisq.core.locale.Res; import bisq.core.offer.messages.SignOfferResponse; import bisq.core.offer.placeoffer.tasks.AddToOfferBook; import bisq.core.offer.placeoffer.tasks.MakerReservesOfferFunds; @@ -24,7 +25,10 @@ import bisq.core.offer.placeoffer.tasks.MakerSendsSignOfferRequest; import bisq.core.offer.placeoffer.tasks.MakerProcessesSignOfferResponse; import bisq.core.offer.placeoffer.tasks.ValidateOffer; import bisq.core.trade.handlers.TransactionResultHandler; +import bisq.core.trade.protocol.TradeProtocol; import bisq.network.p2p.NodeAddress; +import bisq.common.Timer; +import bisq.common.UserThread; import bisq.common.handlers.ErrorMessageHandler; import bisq.common.taskrunner.TaskRunner; @@ -35,6 +39,7 @@ public class PlaceOfferProtocol { private static final Logger log = LoggerFactory.getLogger(PlaceOfferProtocol.class); private final PlaceOfferModel model; + private Timer timeoutTimer; private final TransactionResultHandler resultHandler; private final ErrorMessageHandler errorMessageHandler; @@ -58,14 +63,16 @@ public class PlaceOfferProtocol { public void placeOffer() { log.debug("placeOffer() " + model.getOffer().getId()); + + timeoutTimer = UserThread.runAfter(() -> { + handleError(Res.get("createOffer.timeoutAtPublishing")); + }, TradeProtocol.TRADE_TIMEOUT); + TaskRunner taskRunner = new TaskRunner<>(model, () -> { - log.debug("sequence at placeOffer completed"); }, (errorMessage) -> { - log.error(errorMessage); - model.getOffer().setErrorMessage(errorMessage); - errorMessageHandler.handleErrorMessage(errorMessage); + handleError(errorMessage); } ); taskRunner.addTasks( @@ -81,19 +88,29 @@ public class PlaceOfferProtocol { public void handleSignOfferResponse(SignOfferResponse response, NodeAddress sender) { log.debug("handleSignOfferResponse() " + model.getOffer().getId()); model.setSignOfferResponse(response); - + if (!model.getOffer().getOfferPayload().getArbitratorSigner().equals(sender)) { log.warn("Ignoring sign offer response from different sender"); return; } - + + // ignore if timer already stopped + if (timeoutTimer == null) { + log.warn("Ignoring sign offer response from arbitrator because timeout has expired"); + return; + } + + timeoutTimer = UserThread.runAfter(() -> { + handleError(Res.get("createOffer.timeoutAtPublishing")); + }, TradeProtocol.TRADE_TIMEOUT); + TaskRunner taskRunner = new TaskRunner<>(model, () -> { log.debug("sequence at handleSignOfferResponse completed"); + stopTimeoutTimer(); resultHandler.handleResult(model.getTransaction()); // TODO (woodser): XMR transaction instead }, (errorMessage) -> { - log.error(errorMessage); if (model.isOfferAddedToOfferBook()) { model.getOfferBookService().removeOffer(model.getOffer().getOfferPayload(), () -> { @@ -102,8 +119,7 @@ public class PlaceOfferProtocol { }, log::error); } - model.getOffer().setErrorMessage(errorMessage); - errorMessageHandler.handleErrorMessage(errorMessage); + handleError(errorMessage); } ); taskRunner.addTasks( @@ -113,4 +129,20 @@ public class PlaceOfferProtocol { taskRunner.run(); } + + private void stopTimeoutTimer() { + if (timeoutTimer != null) { + timeoutTimer.stop(); + timeoutTimer = null; + } + } + + private void handleError(String errorMessage) { + if (timeoutTimer != null) { + log.error(errorMessage); + stopTimeoutTimer(); + model.getOffer().setErrorMessage(errorMessage); + errorMessageHandler.handleErrorMessage(errorMessage); + } + } } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java index 7d53bc2ed6..8c3750c66a 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java @@ -50,7 +50,7 @@ import bisq.core.util.coin.CoinFormatter; import bisq.core.util.coin.CoinUtil; import bisq.network.p2p.P2PService; - +import bisq.common.handlers.ErrorMessageHandler; import bisq.common.util.MathUtils; import bisq.common.util.Utilities; @@ -90,8 +90,6 @@ import java.util.stream.Collectors; import lombok.Getter; -import javax.annotation.Nullable; - import static bisq.core.payment.payload.PaymentMethod.HAL_CASH_ID; import static com.google.common.base.Preconditions.checkNotNull; import static java.util.Comparator.comparing; @@ -313,12 +311,12 @@ public abstract class MutableOfferDataModel extends OfferDataModel { paymentAccount); } - void onPlaceOffer(Offer offer, TransactionResultHandler resultHandler) { + void onPlaceOffer(Offer offer, TransactionResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { openOfferManager.placeOffer(offer, useSavingsWallet, triggerPrice, resultHandler, - log::error); + errorMessageHandler); } void onPaymentAccountSelected(PaymentAccount paymentAccount) { diff --git a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java index e4f724a7f4..ce48e9f3cc 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java @@ -58,7 +58,6 @@ import bisq.core.util.validation.AltcoinValidator; import bisq.core.util.validation.FiatPriceValidator; import bisq.core.util.validation.InputValidator; import bisq.core.util.validation.MonetaryValidator; -import bisq.common.Timer; import bisq.common.UserThread; import bisq.common.app.DevEnv; import bisq.common.util.MathUtils; @@ -173,7 +172,6 @@ public abstract class MutableOfferViewModel ext private ChangeListener isWalletFundedListener; private ChangeListener errorMessageListener; protected Offer offer; - private Timer timeoutTimer; private boolean inputIsMarketBasedPrice; private ChangeListener useMarketBasedPriceListener; private boolean ignorePriceStringListener, ignoreVolumeStringListener, ignoreAmountStringListener, ignoreSecurityDepositStringListener; @@ -253,7 +251,6 @@ public abstract class MutableOfferViewModel ext protected void deactivate() { removeBindings(); removeListeners(); - stopTimeoutTimer(); } private void addBindings() { @@ -611,41 +608,19 @@ public abstract class MutableOfferViewModel ext errorMessage.set(null); createOfferRequested = true; - if (timeoutTimer == null) { - timeoutTimer = UserThread.runAfter(() -> { - stopTimeoutTimer(); - createOfferRequested = false; - errorMessage.set(Res.get("createOffer.timeoutAtPublishing")); - - updateButtonDisableState(); - updateSpinnerInfo(); - - resultHandler.run(); - }, 60); - } - errorMessageListener = (observable, oldValue, newValue) -> { - if (newValue != null) { - stopTimeoutTimer(); - createOfferRequested = false; - if (offer.getState() == Offer.State.OFFER_FEE_RESERVED) - errorMessage.set(newValue + Res.get("createOffer.errorInfo")); - else - errorMessage.set(newValue); - - updateButtonDisableState(); - updateSpinnerInfo(); - - resultHandler.run(); - } - }; - - offer.errorMessageProperty().addListener(errorMessageListener); - dataModel.onPlaceOffer(offer, transaction -> { - stopTimeoutTimer(); resultHandler.run(); placeOfferCompleted.set(true); errorMessage.set(null); + }, errMessage -> { + createOfferRequested = false; + if (offer.getState() == Offer.State.OFFER_FEE_RESERVED) errorMessage.set(errMessage + Res.get("createOffer.errorInfo")); + else errorMessage.set(errMessage); + + updateButtonDisableState(); + updateSpinnerInfo(); + + resultHandler.run(); }); updateButtonDisableState(); @@ -1287,13 +1262,6 @@ public abstract class MutableOfferViewModel ext isPlaceOfferButtonDisabled.set(createOfferRequested || !inputDataValid || !dataModel.getIsBtcWalletFunded().get()); } - private void stopTimeoutTimer() { - if (timeoutTimer != null) { - timeoutTimer.stop(); - timeoutTimer = null; - } - } - private CoinFormatter getFormatterForMakerFee() { return btcFormatter; }