From 3b0080dbbabd0787b770964a833f58941e480885 Mon Sep 17 00:00:00 2001 From: woodser Date: Wed, 31 Jul 2024 19:36:15 -0400 Subject: [PATCH] support re-opening dispute if payout fails --- .../haveno/core/api/CoreDisputesService.java | 2 +- .../haveno/core/support/SupportManager.java | 31 ++++- .../haveno/core/support/dispute/Dispute.java | 4 + .../core/support/dispute/DisputeManager.java | 124 ++++++++++++------ .../arbitration/ArbitrationManager.java | 26 ++-- .../main/java/haveno/core/trade/Trade.java | 6 +- .../pendingtrades/PendingTradesDataModel.java | 37 +++--- .../pendingtrades/steps/TradeStepView.java | 26 ++-- .../main/support/dispute/DisputeView.java | 2 +- 9 files changed, 173 insertions(+), 85 deletions(-) diff --git a/core/src/main/java/haveno/core/api/CoreDisputesService.java b/core/src/main/java/haveno/core/api/CoreDisputesService.java index c2eaf09e..d786069a 100644 --- a/core/src/main/java/haveno/core/api/CoreDisputesService.java +++ b/core/src/main/java/haveno/core/api/CoreDisputesService.java @@ -112,7 +112,7 @@ public class CoreDisputesService { // Sends the openNewDisputeMessage to arbitrator, who will then create 2 disputes // one for the opener, the other for the peer, see sendPeerOpenedDisputeMessage. - disputeManager.sendDisputeOpenedMessage(dispute, false, resultHandler, faultHandler); + disputeManager.sendDisputeOpenedMessage(dispute, resultHandler, faultHandler); tradeManager.requestPersistence(); }, trade.getId()); } diff --git a/core/src/main/java/haveno/core/support/SupportManager.java b/core/src/main/java/haveno/core/support/SupportManager.java index 8582c303..caca60ac 100644 --- a/core/src/main/java/haveno/core/support/SupportManager.java +++ b/core/src/main/java/haveno/core/support/SupportManager.java @@ -186,8 +186,7 @@ public abstract class SupportManager { private void onAckMessage(AckMessage ackMessage) { if (ackMessage.getSourceType() == getAckMessageSourceType()) { if (ackMessage.isSuccess()) { - log.info("Received AckMessage for {} with tradeId {} and uid {}", - ackMessage.getSourceMsgClassName(), ackMessage.getSourceId(), ackMessage.getSourceUid()); + log.info("Received AckMessage for {} with tradeId {} and uid {}", ackMessage.getSourceMsgClassName(), ackMessage.getSourceId(), ackMessage.getSourceUid()); // ack message on chat message received when dispute is opened and closed if (ackMessage.getSourceMsgClassName().equals(ChatMessage.class.getSimpleName())) { @@ -195,15 +194,35 @@ public abstract class SupportManager { for (Dispute dispute : trade.getDisputes()) { for (ChatMessage chatMessage : dispute.getChatMessages()) { if (chatMessage.getUid().equals(ackMessage.getSourceUid())) { - if (dispute.isClosed()) trade.pollWalletNormallyForMs(30000); // sync to check for payout - else trade.advanceDisputeState(Trade.DisputeState.DISPUTE_OPENED); + if (trade.getDisputeState() == Trade.DisputeState.DISPUTE_REQUESTED) { + if (dispute.isClosed()) dispute.reOpen(); + trade.advanceDisputeState(Trade.DisputeState.DISPUTE_OPENED); + } else if (dispute.isClosed()) { + trade.pollWalletNormallyForMs(30000); // sync to check for payout + } } } } } } else { - log.warn("Received AckMessage with error state for {} with tradeId {} and errorMessage={}", - ackMessage.getSourceMsgClassName(), ackMessage.getSourceId(), ackMessage.getErrorMessage()); + log.warn("Received AckMessage with error state for {} with tradeId={}, sender={}, errorMessage={}", + ackMessage.getSourceMsgClassName(), ackMessage.getSourceId(), ackMessage.getSenderNodeAddress(), ackMessage.getErrorMessage()); + + // nack message on chat message received when dispute closed message is nacked + if (ackMessage.getSourceMsgClassName().equals(ChatMessage.class.getSimpleName())) { + Trade trade = tradeManager.getTrade(ackMessage.getSourceId()); + for (Dispute dispute : trade.getDisputes()) { + for (ChatMessage chatMessage : dispute.getChatMessages()) { + if (chatMessage.getUid().equals(ackMessage.getSourceUid())) { + if (trade.getDisputeState().isCloseRequested()) { + log.warn("DisputeCloseMessage was nacked. We close the dispute now. tradeId={}, nack sender={}", trade.getId(), ackMessage.getSenderNodeAddress()); + dispute.setIsClosed(); + trade.advanceDisputeState(Trade.DisputeState.DISPUTE_CLOSED); + } + } + } + } + } } getAllChatMessages(ackMessage.getSourceId()).stream() diff --git a/core/src/main/java/haveno/core/support/dispute/Dispute.java b/core/src/main/java/haveno/core/support/dispute/Dispute.java index 2143561b..cd7e13d0 100644 --- a/core/src/main/java/haveno/core/support/dispute/Dispute.java +++ b/core/src/main/java/haveno/core/support/dispute/Dispute.java @@ -77,6 +77,10 @@ public final class Dispute implements NetworkPayload, PersistablePayload { REOPENED, CLOSED; + public boolean isOpen() { + return this == NEW || this == OPEN || this == REOPENED; + } + public static Dispute.State fromProto(protobuf.Dispute.State state) { return ProtoUtil.enumFromProto(Dispute.State.class, state.name()); } diff --git a/core/src/main/java/haveno/core/support/dispute/DisputeManager.java b/core/src/main/java/haveno/core/support/dispute/DisputeManager.java index 7aa1906c..bd019e12 100644 --- a/core/src/main/java/haveno/core/support/dispute/DisputeManager.java +++ b/core/src/main/java/haveno/core/support/dispute/DisputeManager.java @@ -326,7 +326,6 @@ public abstract class DisputeManager> extends Sup // trader sends message to arbitrator to open dispute public void sendDisputeOpenedMessage(Dispute dispute, - boolean reOpen, ResultHandler resultHandler, FaultHandler faultHandler) { @@ -356,7 +355,16 @@ public abstract class DisputeManager> extends Sup } Optional storedDisputeOptional = findDispute(dispute); + boolean reOpen = storedDisputeOptional.isPresent() && storedDisputeOptional.get().isClosed(); if (!storedDisputeOptional.isPresent() || reOpen) { + + // add or re-open dispute + if (reOpen) { + dispute = storedDisputeOptional.get(); + } else { + disputeList.add(dispute); + } + String disputeInfo = getDisputeInfo(dispute); String sysMsg = dispute.isSupportTicket() ? Res.get("support.youOpenedTicket", disputeInfo, Version.VERSION) : @@ -371,9 +379,6 @@ public abstract class DisputeManager> extends Sup p2PService.getAddress()); chatMessage.setSystemMessage(true); dispute.addAndPersistChatMessage(chatMessage); - if (!reOpen) { - disputeList.add(dispute); - } // create dispute opened message trade.exportMultisigHex(); @@ -392,6 +397,7 @@ public abstract class DisputeManager> extends Sup recordPendingMessage(disputeOpenedMessage.getClass().getSimpleName()); // send dispute opened message + trade.setDisputeState(Trade.DisputeState.DISPUTE_REQUESTED); mailboxMessageService.sendEncryptedMailboxMessage(agentNodeAddress, dispute.getAgentPubKeyRing(), disputeOpenedMessage, @@ -425,7 +431,6 @@ public abstract class DisputeManager> extends Sup // We use the chatMessage wrapped inside the openNewDisputeMessage for // the state, as that is displayed to the user and we only persist that msg chatMessage.setStoredInMailbox(true); - trade.advanceDisputeState(Trade.DisputeState.DISPUTE_REQUESTED); requestPersistence(); resultHandler.handleResult(); } @@ -442,6 +447,7 @@ public abstract class DisputeManager> extends Sup // We use the chatMessage wrapped inside the openNewDisputeMessage for // the state, as that is displayed to the user and we only persist that msg chatMessage.setSendMessageError(errorMessage); + trade.setDisputeState(Trade.DisputeState.NO_DISPUTE); requestPersistence(); faultHandler.handleFault("Sending dispute message failed: " + errorMessage, new DisputeMessageDeliveryFailedException()); @@ -460,16 +466,30 @@ public abstract class DisputeManager> extends Sup // arbitrator receives dispute opened message from opener, opener's peer receives from arbitrator protected void handleDisputeOpenedMessage(DisputeOpenedMessage message) { - Dispute dispute = message.getDispute(); - log.info("Processing {} with trade {}, dispute {}", message.getClass().getSimpleName(), dispute.getTradeId(), dispute.getId()); + Dispute msgDispute = message.getDispute(); + log.info("Processing {} with trade {}, dispute {}", message.getClass().getSimpleName(), msgDispute.getTradeId(), msgDispute.getId()); // get trade - Trade trade = tradeManager.getTrade(dispute.getTradeId()); + Trade trade = tradeManager.getTrade(msgDispute.getTradeId()); if (trade == null) { - log.warn("Dispute trade {} does not exist", dispute.getTradeId()); + log.warn("Dispute trade {} does not exist", msgDispute.getTradeId()); + return; + } + if (trade.isPayoutPublished()) { + log.warn("Dispute trade {} payout already published", msgDispute.getTradeId()); return; } + // find existing dispute + Optional storedDisputeOptional = findDispute(msgDispute); + + // determine if re-opening dispute + boolean reOpen = storedDisputeOptional.isPresent() && storedDisputeOptional.get().isClosed(); + + // use existing dispute or create new + Dispute dispute = reOpen ? storedDisputeOptional.get() : msgDispute; + + // process on trade thread ThreadUtils.execute(() -> { synchronized (trade) { String errorMessage = null; @@ -508,14 +528,20 @@ public abstract class DisputeManager> extends Sup } // get sender - senderPubKeyRing = trade.isArbitrator() ? (dispute.isDisputeOpenerIsBuyer() ? contract.getBuyerPubKeyRing() : contract.getSellerPubKeyRing()) : trade.getArbitrator().getPubKeyRing(); - TradePeer sender = trade.getTradePeer(senderPubKeyRing); + TradePeer sender; + if (reOpen) { // re-open can come from either peer + sender = trade.isArbitrator() ? trade.getTradePeer(message.getSenderNodeAddress()) : trade.getArbitrator(); + senderPubKeyRing = sender.getPubKeyRing(); + } else { + senderPubKeyRing = trade.isArbitrator() ? (dispute.isDisputeOpenerIsBuyer() ? contract.getBuyerPubKeyRing() : contract.getSellerPubKeyRing()) : trade.getArbitrator().getPubKeyRing(); + sender = trade.getTradePeer(senderPubKeyRing); + } if (sender == null) throw new RuntimeException("Pub key ring is not from arbitrator, buyer, or seller"); // update sender node address sender.setNodeAddress(message.getSenderNodeAddress()); - // message to trader is expected from arbitrator + // verify message to trader is expected from arbitrator if (!trade.isArbitrator() && sender != trade.getArbitrator()) { throw new RuntimeException(message.getClass().getSimpleName() + " to trader is expected only from arbitrator"); } @@ -533,16 +559,29 @@ public abstract class DisputeManager> extends Sup // add chat message with price info if (trade instanceof ArbitratorTrade) addPriceInfoMessage(dispute, 0); - // add dispute + // add or re-open dispute synchronized (disputeList) { - if (!disputeList.contains(dispute)) { - Optional storedDisputeOptional = findDispute(dispute); - if (!storedDisputeOptional.isPresent()) { - disputeList.add(dispute); - trade.advanceDisputeState(Trade.DisputeState.DISPUTE_OPENED); + if (!disputeList.contains(msgDispute)) { + if (!storedDisputeOptional.isPresent() || reOpen) { - // send dispute opened message to peer if arbitrator - if (trade.isArbitrator()) sendDisputeOpenedMessageToPeer(dispute, contract, dispute.isDisputeOpenerIsBuyer() ? contract.getSellerPubKeyRing() : contract.getBuyerPubKeyRing(), trade.getSelf().getUpdatedMultisigHex()); + // update trade state + if (reOpen) { + trade.setDisputeState(Trade.DisputeState.DISPUTE_OPENED); + } else { + disputeList.add(dispute); + trade.advanceDisputeState(Trade.DisputeState.DISPUTE_OPENED); + } + + // reset buyer and seller unsigned payout tx hex + trade.getBuyer().setUnsignedPayoutTxHex(null); + trade.getSeller().setUnsignedPayoutTxHex(null); + + // send dispute opened message to other peer if arbitrator + if (trade.isArbitrator()) { + TradePeer senderPeer = sender == trade.getMaker() ? trade.getTaker() : trade.getMaker(); + if (senderPeer != trade.getMaker() && senderPeer != trade.getTaker()) throw new RuntimeException("Sender peer is not maker or taker, address=" + senderPeer.getNodeAddress()); + sendDisputeOpenedMessageToPeer(dispute, contract, senderPeer.getPubKeyRing(), trade.getSelf().getUpdatedMultisigHex()); + } tradeManager.requestPersistence(); errorMessage = null; } else { @@ -553,7 +592,7 @@ public abstract class DisputeManager> extends Sup // add chat message with mediation info if applicable addMediationResultMessage(dispute); } else { - throw new RuntimeException("We got a dispute msg that we have already stored. TradeId = " + dispute.getTradeId()); + throw new RuntimeException("We got a dispute msg that we have already stored. TradeId = " + msgDispute.getTradeId()); } } } catch (Exception e) { @@ -566,7 +605,7 @@ public abstract class DisputeManager> extends Sup // use chat message instead of open dispute message for the ack ObservableList messages = message.getDispute().getChatMessages(); if (!messages.isEmpty()) { - ChatMessage msg = messages.get(0); + ChatMessage msg = messages.get(messages.size() - 1); // send ack to sender of last chat message sendAckMessage(msg, senderPubKeyRing, errorMessage == null, errorMessage); } @@ -580,7 +619,7 @@ public abstract class DisputeManager> extends Sup Contract contractFromOpener, PubKeyRing pubKeyRing, String updatedMultisigHex) { - log.info("{}.sendPeerOpenedDisputeMessage() with trade {}, dispute {}", getClass().getSimpleName(), disputeFromOpener.getTradeId(), disputeFromOpener.getId()); + log.info("{} sendPeerOpenedDisputeMessage() with trade {}, dispute {}", getClass().getSimpleName(), disputeFromOpener.getTradeId(), disputeFromOpener.getId()); // We delay a bit for sending the message to the peer to allow that a openDispute message from the peer is // being used as the valid msg. If dispute agent was offline and both peer requested we want to see the correct // message and not skip the system message of the peer as it would be the case if we have created the system msg @@ -602,6 +641,7 @@ public abstract class DisputeManager> extends Sup return; } + // create mirrored dispute Dispute dispute = new Dispute(new Date().getTime(), disputeFromOpener.getTradeId(), pubKeyRing.hashCode(), @@ -627,10 +667,9 @@ public abstract class DisputeManager> extends Sup dispute.setDelayedPayoutTxId(disputeFromOpener.getDelayedPayoutTxId()); dispute.setDonationAddressOfDelayedPayoutTx(disputeFromOpener.getDonationAddressOfDelayedPayoutTx()); + // skip if dispute already open Optional storedDisputeOptional = findDispute(dispute); - - // Valid case if both have opened a dispute and agent was not online. - if (storedDisputeOptional.isPresent()) { + if (storedDisputeOptional.isPresent() && !storedDisputeOptional.get().isClosed()) { log.info("We got a dispute already open for that trade and trading peer. TradeId = {}", dispute.getTradeId()); return; } @@ -652,8 +691,15 @@ public abstract class DisputeManager> extends Sup addPriceInfoMessage(dispute, 0); - synchronized (disputeList) { - disputeList.add(dispute); + // add or re-open dispute + boolean reOpen = storedDisputeOptional.isPresent() && storedDisputeOptional.get().isClosed(); + if (reOpen) { + dispute = storedDisputeOptional.get(); + dispute.reOpen(); + } else { + synchronized (disputeList) { + disputeList.add(dispute); + } } // get trade @@ -663,10 +709,10 @@ public abstract class DisputeManager> extends Sup return; } - // We mirrored dispute already! - Contract contract = dispute.getContract(); - PubKeyRing peersPubKeyRing = dispute.isDisputeOpenerIsBuyer() ? contract.getSellerPubKeyRing() : contract.getBuyerPubKeyRing(); - NodeAddress peersNodeAddress = dispute.isDisputeOpenerIsBuyer() ? contract.getSellerNodeAddress() : contract.getBuyerNodeAddress(); + // create dispute opened message with peer dispute + TradePeer peer = trade.getTradePeer(pubKeyRing); + PubKeyRing peersPubKeyRing = peer.getPubKeyRing(); + NodeAddress peersNodeAddress = peer.getNodeAddress(); DisputeOpenedMessage peerOpenedDisputeMessage = new DisputeOpenedMessage(dispute, p2PService.getAddress(), UUID.randomUUID().toString(), @@ -754,7 +800,7 @@ public abstract class DisputeManager> extends Sup dispute.addAndPersistChatMessage(chatMessage); } - // create dispute payout tx once per trader if we have their updated multisig hex + // create dispute payout tx TradePeer receiver = trade.getTradePeer(dispute.getTraderPubKeyRing()); if (!trade.isPayoutPublished() && receiver.getUpdatedMultisigHex() != null && receiver.getUnsignedPayoutTxHex() == null) { createDisputePayoutTx(trade, dispute.getContract(), disputeResult, true); @@ -906,8 +952,8 @@ public abstract class DisputeManager> extends Sup if (updateState) { trade.getProcessModel().setUnsignedPayoutTx(payoutTx); trade.updatePayout(payoutTx); - if (trade.getBuyer().getUpdatedMultisigHex() != null && trade.getBuyer().getUnsignedPayoutTxHex() == null) trade.getBuyer().setUnsignedPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex()); - if (trade.getSeller().getUpdatedMultisigHex() != null && trade.getSeller().getUnsignedPayoutTxHex() == null) trade.getSeller().setUnsignedPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex()); + if (trade.getBuyer().getUpdatedMultisigHex() != null) trade.getBuyer().setUnsignedPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex()); + if (trade.getSeller().getUpdatedMultisigHex() != null) trade.getSeller().setUnsignedPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex()); } trade.requestPersistence(); return payoutTx; @@ -942,21 +988,21 @@ public abstract class DisputeManager> extends Sup return keyRing.getPubKeyRing().equals(dispute.getAgentPubKeyRing()); } - private Optional findDispute(Dispute dispute) { + public Optional findDispute(Dispute dispute) { return findDispute(dispute.getTradeId(), dispute.getTraderId()); } - protected Optional findDispute(DisputeResult disputeResult) { + public Optional findDispute(DisputeResult disputeResult) { ChatMessage chatMessage = disputeResult.getChatMessage(); checkNotNull(chatMessage, "chatMessage must not be null"); return findDispute(disputeResult.getTradeId(), disputeResult.getTraderId()); } - private Optional findDispute(ChatMessage message) { + public Optional findDispute(ChatMessage message) { return findDispute(message.getTradeId(), message.getTraderId()); } - protected Optional findDispute(String tradeId, int traderId) { + public Optional findDispute(String tradeId, int traderId) { T disputeList = getDisputeList(); if (disputeList == null) { log.warn("disputes is null"); diff --git a/core/src/main/java/haveno/core/support/dispute/arbitration/ArbitrationManager.java b/core/src/main/java/haveno/core/support/dispute/arbitration/ArbitrationManager.java index c3ec00b5..91316239 100644 --- a/core/src/main/java/haveno/core/support/dispute/arbitration/ArbitrationManager.java +++ b/core/src/main/java/haveno/core/support/dispute/arbitration/ArbitrationManager.java @@ -308,7 +308,7 @@ public final class ArbitrationManager extends DisputeManager= DisputeState.ARBITRATOR_SENT_DISPUTE_CLOSED_MSG.ordinal(); } public boolean isClosed() { diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java index 1e0072b6..944c977b 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java @@ -37,7 +37,6 @@ import haveno.core.offer.OfferUtil; import haveno.core.payment.payload.PaymentAccountPayload; import haveno.core.support.SupportType; import haveno.core.support.dispute.Dispute; -import haveno.core.support.dispute.DisputeAlreadyOpenException; import haveno.core.support.dispute.DisputeList; import haveno.core.support.dispute.DisputeManager; import haveno.core.support.dispute.arbitration.ArbitrationManager; @@ -67,6 +66,7 @@ import haveno.network.p2p.P2PService; import java.math.BigInteger; import java.util.Date; import java.util.HashSet; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import javafx.beans.property.ObjectProperty; @@ -545,33 +545,38 @@ public class PendingTradesDataModel extends ActivatableDataModel { dispute.setExtraData("counterCurrencyExtraData", trade.getCounterCurrencyExtraData()); trade.setDisputeState(Trade.DisputeState.MEDIATION_REQUESTED); - sendDisputeOpenedMessage(dispute, false, disputeManager); + sendDisputeOpenedMessage(dispute, disputeManager); tradeManager.requestPersistence(); } else if (useArbitration) { // Only if we have completed mediation we allow arbitration disputeManager = arbitrationManager; Dispute dispute = disputesService.createDisputeForTrade(trade, offer, pubKeyRingProvider.get(), isMaker, isSupportTicket); trade.exportMultisigHex(); - sendDisputeOpenedMessage(dispute, false, disputeManager); + sendDisputeOpenedMessage(dispute, disputeManager); tradeManager.requestPersistence(); } else { log.warn("Invalid dispute state {}", disputeState.name()); } } - private void sendDisputeOpenedMessage(Dispute dispute, boolean reOpen, DisputeManager> disputeManager) { - disputeManager.sendDisputeOpenedMessage(dispute, reOpen, - () -> navigation.navigateTo(MainView.class, SupportView.class, ArbitrationClientView.class), (errorMessage, throwable) -> { - if ((throwable instanceof DisputeAlreadyOpenException)) { - errorMessage += "\n\n" + Res.get("portfolio.pending.openAgainDispute.msg"); - new Popup().warning(errorMessage) - .actionButtonText(Res.get("portfolio.pending.openAgainDispute.button")) - .onAction(() -> sendDisputeOpenedMessage(dispute, true, disputeManager)) - .closeButtonText(Res.get("shared.cancel")).show(); - } else { - new Popup().warning(errorMessage).show(); - } - }); + private void sendDisputeOpenedMessage(Dispute dispute, DisputeManager> disputeManager) { + Optional optionalDispute = disputeManager.findDispute(dispute); + boolean disputeClosed = optionalDispute.isPresent() && optionalDispute.get().isClosed(); + if (disputeClosed) { + String msg = "We got a dispute already open for that trade and trading peer.\n" + "TradeId = " + dispute.getTradeId(); + new Popup().warning(msg + "\n\n" + Res.get("portfolio.pending.openAgainDispute.msg")) + .actionButtonText(Res.get("portfolio.pending.openAgainDispute.button")) + .onAction(() -> doSendDisputeOpenedMessage(dispute, disputeManager)) + .closeButtonText(Res.get("shared.cancel")).show(); + } else { + doSendDisputeOpenedMessage(dispute, disputeManager); + } + } + + private void doSendDisputeOpenedMessage(Dispute dispute, DisputeManager> disputeManager) { + disputeManager.sendDisputeOpenedMessage(dispute, + () -> navigation.navigateTo(MainView.class, SupportView.class, ArbitrationClientView.class), + (errorMessage, throwable) -> new Popup().warning(errorMessage).show()); } public boolean isReadyForTxBroadcast() { diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/TradeStepView.java b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/TradeStepView.java index 5a83d591..f06f615e 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/TradeStepView.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/TradeStepView.java @@ -190,15 +190,13 @@ public abstract class TradeStepView extends AnchorPane { } trade.errorMessageProperty().addListener(errorMessageListener); - if (!isMediationClosedState()) { - tradeStepInfo.setOnAction(e -> { - if (this.isTradePeriodOver()) { - openSupportTicket(); - } else { - openChat(); - } - }); - } + tradeStepInfo.setOnAction(e -> { + if (!isArbitrationOpenedState() && this.isTradePeriodOver()) { + openSupportTicket(); + } else { + openChat(); + } + }); // We get mailbox messages processed after we have bootstrapped. This will influence the states we // handle in our disputeStateSubscription and mediationResultStateSubscriptions. To avoid that we show @@ -572,7 +570,7 @@ public abstract class TradeStepView extends AnchorPane { } private void updateMediationResultState(boolean blockOpeningOfResultAcceptedPopup) { - if (isInArbitration()) { + if (isInMediation()) { if (isRefundRequestStartedByPeer()) { tradeStepInfo.setState(TradeStepInfo.State.IN_REFUND_REQUEST_PEER_REQUESTED); } else if (isRefundRequestSelfStarted()) { @@ -597,7 +595,7 @@ public abstract class TradeStepView extends AnchorPane { } } - private boolean isInArbitration() { + private boolean isInMediation() { return isRefundRequestStartedByPeer() || isRefundRequestSelfStarted(); } @@ -613,6 +611,10 @@ public abstract class TradeStepView extends AnchorPane { return trade.getDisputeState() == Trade.DisputeState.MEDIATION_CLOSED; } + private boolean isArbitrationOpenedState() { + return trade.getDisputeState().isOpen(); + } + private boolean isTradePeriodOver() { return Trade.TradePeriodState.TRADE_PERIOD_OVER == trade.tradePeriodStateProperty().get(); } @@ -741,7 +743,7 @@ public abstract class TradeStepView extends AnchorPane { } private void updateTradePeriodState(Trade.TradePeriodState tradePeriodState) { - if (trade.getDisputeState() == Trade.DisputeState.NO_DISPUTE) { + if (!trade.getDisputeState().isOpen()) { switch (tradePeriodState) { case FIRST_HALF: // just for dev testing. not possible to go back in time ;-) diff --git a/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java b/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java index a5fd1f72..9d88ad23 100644 --- a/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java +++ b/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java @@ -1485,7 +1485,7 @@ public abstract class DisputeView extends ActivatableView implements @Override public void onCloseDisputeFromChatWindow(Dispute dispute) { - if (dispute.getDisputeState() == Dispute.State.NEW || dispute.getDisputeState() == Dispute.State.OPEN) { + if (dispute.getDisputeState() == Dispute.State.NEW || dispute.getDisputeState().isOpen()) { handleOnProcessDispute(dispute); } else { closeDisputeFromButton();