resend dispute closed message with payout tx on updated multisig info

advance dispute state if progress
SupportManager processes messages in order
re-arrange domain initialization to fix null pub key in DisputeManager
This commit is contained in:
woodser 2023-01-23 14:25:28 -05:00
parent 533639bd19
commit 23393bb4b8
7 changed files with 176 additions and 46 deletions

View file

@ -176,6 +176,10 @@ public class DomainInitialisation {
PersistenceManager.onAllServicesInitialized(); PersistenceManager.onAllServicesInitialized();
arbitratorManager.onAllServicesInitialized();
mediatorManager.onAllServicesInitialized();
refundAgentManager.onAllServicesInitialized();
tradeManager.onAllServicesInitialized(); tradeManager.onAllServicesInitialized();
arbitrationManager.onAllServicesInitialized(); arbitrationManager.onAllServicesInitialized();
mediationManager.onAllServicesInitialized(); mediationManager.onAllServicesInitialized();
@ -192,10 +196,6 @@ public class DomainInitialisation {
walletAppSetup.setRejectedTxErrorMessageHandler(rejectedTxErrorMessageHandler, openOfferManager, tradeManager); walletAppSetup.setRejectedTxErrorMessageHandler(rejectedTxErrorMessageHandler, openOfferManager, tradeManager);
arbitratorManager.onAllServicesInitialized();
mediatorManager.onAllServicesInitialized();
refundAgentManager.onAllServicesInitialized();
privateNotificationManager.privateNotificationProperty().addListener((observable, oldValue, newValue) -> { privateNotificationManager.privateNotificationProperty().addListener((observable, oldValue, newValue) -> {
if (displayPrivateNotificationHandler != null) if (displayPrivateNotificationHandler != null)
displayPrivateNotificationHandler.accept(newValue); displayPrivateNotificationHandler.accept(newValue);

View file

@ -22,13 +22,15 @@ import bisq.core.api.CoreNotificationService;
import bisq.core.locale.Res; import bisq.core.locale.Res;
import bisq.core.support.messages.ChatMessage; import bisq.core.support.messages.ChatMessage;
import bisq.core.support.messages.SupportMessage; import bisq.core.support.messages.SupportMessage;
import bisq.core.trade.protocol.TradeProtocol;
import bisq.core.trade.protocol.TradeProtocol.MailboxMessageComparator;
import bisq.network.p2p.AckMessage; import bisq.network.p2p.AckMessage;
import bisq.network.p2p.AckMessageSourceType; import bisq.network.p2p.AckMessageSourceType;
import bisq.network.p2p.DecryptedMessageWithPubKey; import bisq.network.p2p.DecryptedMessageWithPubKey;
import bisq.network.p2p.NodeAddress; import bisq.network.p2p.NodeAddress;
import bisq.network.p2p.P2PService; import bisq.network.p2p.P2PService;
import bisq.network.p2p.SendMailboxMessageListener; import bisq.network.p2p.SendMailboxMessageListener;
import bisq.network.p2p.mailbox.MailboxMessage;
import bisq.network.p2p.mailbox.MailboxMessageService; import bisq.network.p2p.mailbox.MailboxMessageService;
import bisq.common.Timer; import bisq.common.Timer;
@ -36,6 +38,7 @@ import bisq.common.UserThread;
import bisq.common.crypto.PubKeyRing; import bisq.common.crypto.PubKeyRing;
import bisq.common.proto.network.NetworkEnvelope; import bisq.common.proto.network.NetworkEnvelope;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -322,9 +325,27 @@ public abstract class SupportManager {
private void applyMessages() { private void applyMessages() {
synchronized (lock) { synchronized (lock) {
decryptedDirectMessageWithPubKeys.forEach(decryptedMessageWithPubKey -> applyDirectMessage(decryptedMessageWithPubKey));
// apply non-mailbox messages
decryptedDirectMessageWithPubKeys.stream()
.filter(e -> !(e.getNetworkEnvelope() instanceof MailboxMessage))
.forEach(decryptedMessageWithPubKey -> applyDirectMessage(decryptedMessageWithPubKey));
decryptedMailboxMessageWithPubKeys.stream()
.filter(e -> !(e.getNetworkEnvelope() instanceof MailboxMessage))
.forEach(decryptedMessageWithPubKey -> applyMailboxMessage(decryptedMessageWithPubKey));
// apply mailbox messages in order
decryptedDirectMessageWithPubKeys.stream()
.filter(e -> (e.getNetworkEnvelope() instanceof MailboxMessage))
.sorted(new DecryptedMessageWithPubKeyComparator())
.forEach(decryptedMessageWithPubKey -> applyDirectMessage(decryptedMessageWithPubKey));
decryptedMailboxMessageWithPubKeys.stream()
.filter(e -> (e.getNetworkEnvelope() instanceof MailboxMessage))
.sorted(new DecryptedMessageWithPubKeyComparator())
.forEach(decryptedMessageWithPubKey -> applyMailboxMessage(decryptedMessageWithPubKey));
// clear messages
decryptedDirectMessageWithPubKeys.clear(); decryptedDirectMessageWithPubKeys.clear();
decryptedMailboxMessageWithPubKeys.forEach(decryptedMessageWithPubKey -> applyMailboxMessage(decryptedMessageWithPubKey));
decryptedMailboxMessageWithPubKeys.clear(); decryptedMailboxMessageWithPubKeys.clear();
} }
} }
@ -351,4 +372,22 @@ public abstract class SupportManager {
mailboxMessageService.removeMailboxMsg(ackMessage); mailboxMessageService.removeMailboxMsg(ackMessage);
} }
} }
private static class DecryptedMessageWithPubKeyComparator implements Comparator<DecryptedMessageWithPubKey> {
MailboxMessageComparator mailboxMessageComparator;
public DecryptedMessageWithPubKeyComparator() {
mailboxMessageComparator = new TradeProtocol.MailboxMessageComparator();
}
@Override
public int compare(DecryptedMessageWithPubKey m1, DecryptedMessageWithPubKey m2) {
if (m1.getNetworkEnvelope() instanceof MailboxMessage) {
if (m2.getNetworkEnvelope() instanceof MailboxMessage) return mailboxMessageComparator.compare((MailboxMessage) m1.getNetworkEnvelope(), (MailboxMessage) m2.getNetworkEnvelope());
else return 1;
} else {
return m2.getNetworkEnvelope() instanceof MailboxMessage ? -1 : 0;
}
}
}
} }

View file

@ -97,7 +97,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
protected final TradeManager tradeManager; protected final TradeManager tradeManager;
protected final ClosedTradableManager closedTradableManager; protected final ClosedTradableManager closedTradableManager;
protected final OpenOfferManager openOfferManager; protected final OpenOfferManager openOfferManager;
protected final PubKeyRing pubKeyRing; protected final KeyRing keyRing;
protected final DisputeListService<T> disputeListService; protected final DisputeListService<T> disputeListService;
private final Config config; private final Config config;
private final PriceFeedService priceFeedService; private final PriceFeedService priceFeedService;
@ -105,9 +105,6 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
@Getter @Getter
protected final ObservableList<TradeDataValidation.ValidationException> validationExceptions = protected final ObservableList<TradeDataValidation.ValidationException> validationExceptions =
FXCollections.observableArrayList(); FXCollections.observableArrayList();
@Getter
private final KeyPair signatureKeyPair;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Constructor // Constructor
@ -132,18 +129,20 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
this.tradeManager = tradeManager; this.tradeManager = tradeManager;
this.closedTradableManager = closedTradableManager; this.closedTradableManager = closedTradableManager;
this.openOfferManager = openOfferManager; this.openOfferManager = openOfferManager;
this.pubKeyRing = keyRing.getPubKeyRing(); this.keyRing = keyRing;
signatureKeyPair = keyRing.getSignatureKeyPair();
this.disputeListService = disputeListService; this.disputeListService = disputeListService;
this.config = config; this.config = config;
this.priceFeedService = priceFeedService; this.priceFeedService = priceFeedService;
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Implement template methods // Implement template methods
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public KeyPair getSignatureKeyPair() {
return keyRing.getSignatureKeyPair();
}
@Override @Override
public void requestPersistence() { public void requestPersistence() {
disputeListService.requestPersistence(); disputeListService.requestPersistence();
@ -204,7 +203,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
// Abstract methods // Abstract methods
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// We get that message at both peers. The dispute object is in context of the trader // We get this message at both peers. The dispute object is in context of the trader
public abstract void handleDisputeClosedMessage(DisputeClosedMessage disputeClosedMessage); public abstract void handleDisputeClosedMessage(DisputeClosedMessage disputeClosedMessage);
public abstract NodeAddress getAgentNodeAddress(Dispute dispute); public abstract NodeAddress getAgentNodeAddress(Dispute dispute);
@ -292,7 +291,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
} }
public boolean isTrader(Dispute dispute) { public boolean isTrader(Dispute dispute) {
return pubKeyRing.equals(dispute.getTraderPubKeyRing()); return keyRing.getPubKeyRing().equals(dispute.getTraderPubKeyRing());
} }
public Optional<Dispute> findOwnDispute(String tradeId) { public Optional<Dispute> findOwnDispute(String tradeId) {
@ -352,7 +351,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
ChatMessage chatMessage = new ChatMessage( ChatMessage chatMessage = new ChatMessage(
getSupportType(), getSupportType(),
dispute.getTradeId(), dispute.getTradeId(),
pubKeyRing.hashCode(), keyRing.getPubKeyRing().hashCode(),
false, false,
Res.get("support.systemMsg", sysMsg), Res.get("support.systemMsg", sysMsg),
p2PService.getAddress()); p2PService.getAddress());
@ -389,7 +388,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
// We use the chatMessage wrapped inside the openNewDisputeMessage for // We use the chatMessage wrapped inside the openNewDisputeMessage for
// the state, as that is displayed to the user and we only persist that msg // the state, as that is displayed to the user and we only persist that msg
chatMessage.setArrived(true); chatMessage.setArrived(true);
trade.setDisputeState(Trade.DisputeState.DISPUTE_OPENED); trade.setDisputeStateIfProgress(Trade.DisputeState.DISPUTE_OPENED);
requestPersistence(); requestPersistence();
resultHandler.handleResult(); resultHandler.handleResult();
} }
@ -405,7 +404,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
// We use the chatMessage wrapped inside the openNewDisputeMessage for // We use the chatMessage wrapped inside the openNewDisputeMessage for
// the state, as that is displayed to the user and we only persist that msg // the state, as that is displayed to the user and we only persist that msg
chatMessage.setStoredInMailbox(true); chatMessage.setStoredInMailbox(true);
trade.setDisputeState(Trade.DisputeState.DISPUTE_OPENED); trade.setDisputeStateIfProgress(Trade.DisputeState.DISPUTE_OPENED);
requestPersistence(); requestPersistence();
resultHandler.handleResult(); resultHandler.handleResult();
} }
@ -507,7 +506,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
Optional<Dispute> storedDisputeOptional = findDispute(dispute); Optional<Dispute> storedDisputeOptional = findDispute(dispute);
if (!storedDisputeOptional.isPresent()) { if (!storedDisputeOptional.isPresent()) {
disputeList.add(dispute); disputeList.add(dispute);
trade.setDisputeState(Trade.DisputeState.DISPUTE_OPENED); trade.setDisputeStateIfProgress(Trade.DisputeState.DISPUTE_OPENED);
// send dispute opened message to peer if arbitrator // send dispute opened message to peer if arbitrator
if (trade.isArbitrator()) sendDisputeOpenedMessageToPeer(dispute, contract, dispute.isDisputeOpenerIsBuyer() ? contract.getSellerPubKeyRing() : contract.getBuyerPubKeyRing(), trade.getSelf().getUpdatedMultisigHex()); if (trade.isArbitrator()) sendDisputeOpenedMessageToPeer(dispute, contract, dispute.isDisputeOpenerIsBuyer() ? contract.getSellerPubKeyRing() : contract.getBuyerPubKeyRing(), trade.getSelf().getUpdatedMultisigHex());
@ -703,7 +702,9 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
// create dispute payout tx if not given // create dispute payout tx if not given
if (payoutTx == null) payoutTx = createDisputePayoutTx(trade, dispute, disputeResult, false); // can be null if already published or we don't have receiver's multisig hex if (payoutTx == null) payoutTx = createDisputePayoutTx(trade, dispute, disputeResult, false); // can be null if already published or we don't have receiver's multisig hex
// persist result in dispute's chat message // persist result in dispute's chat message once
boolean resending = disputeResult.getChatMessage() != null;
if (!resending) {
ChatMessage chatMessage = new ChatMessage( ChatMessage chatMessage = new ChatMessage(
getSupportType(), getSupportType(),
dispute.getTradeId(), dispute.getTradeId(),
@ -713,12 +714,13 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
p2PService.getAddress()); p2PService.getAddress());
disputeResult.setChatMessage(chatMessage); disputeResult.setChatMessage(chatMessage);
dispute.addAndPersistChatMessage(chatMessage); dispute.addAndPersistChatMessage(chatMessage);
}
// create dispute closed message // create dispute closed message
TradingPeer receiver = trade.getTradingPeer(dispute.getTraderPubKeyRing()); TradingPeer receiver = trade.getTradingPeer(dispute.getTraderPubKeyRing());
String unsignedPayoutTxHex = payoutTx == null ? null : payoutTx.getTxSet().getMultisigTxHex(); String unsignedPayoutTxHex = payoutTx == null ? null : payoutTx.getTxSet().getMultisigTxHex();
TradingPeer receiverPeer = receiver == trade.getBuyer() ? trade.getSeller() : trade.getBuyer(); TradingPeer receiverPeer = receiver == trade.getBuyer() ? trade.getSeller() : trade.getBuyer();
boolean deferPublishPayout = unsignedPayoutTxHex != null && receiverPeer.getUpdatedMultisigHex() != null && trade.getDisputeState().ordinal() >= Trade.DisputeState.ARBITRATOR_SAW_ARRIVED_DISPUTE_CLOSED_MSG.ordinal() ; boolean deferPublishPayout = !resending && unsignedPayoutTxHex != null && receiverPeer.getUpdatedMultisigHex() != null && trade.getDisputeState().ordinal() >= Trade.DisputeState.ARBITRATOR_SAW_ARRIVED_DISPUTE_CLOSED_MSG.ordinal() ;
DisputeClosedMessage disputeClosedMessage = new DisputeClosedMessage(disputeResult, DisputeClosedMessage disputeClosedMessage = new DisputeClosedMessage(disputeResult,
p2PService.getAddress(), p2PService.getAddress(),
UUID.randomUUID().toString(), UUID.randomUUID().toString(),
@ -731,7 +733,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
log.info("Send {} to trader {}. tradeId={}, {}.uid={}, chatMessage.uid={}", log.info("Send {} to trader {}. tradeId={}, {}.uid={}, chatMessage.uid={}",
disputeClosedMessage.getClass().getSimpleName(), receiver.getNodeAddress(), disputeClosedMessage.getClass().getSimpleName(), receiver.getNodeAddress(),
disputeClosedMessage.getClass().getSimpleName(), disputeClosedMessage.getTradeId(), disputeClosedMessage.getClass().getSimpleName(), disputeClosedMessage.getTradeId(),
disputeClosedMessage.getUid(), chatMessage.getUid()); disputeClosedMessage.getUid(), disputeResult.getChatMessage().getUid());
mailboxMessageService.sendEncryptedMailboxMessage(receiver.getNodeAddress(), mailboxMessageService.sendEncryptedMailboxMessage(receiver.getNodeAddress(),
dispute.getTraderPubKeyRing(), dispute.getTraderPubKeyRing(),
disputeClosedMessage, disputeClosedMessage,
@ -742,11 +744,11 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
"chatMessage.uid={}", "chatMessage.uid={}",
disputeClosedMessage.getClass().getSimpleName(), receiver.getNodeAddress(), disputeClosedMessage.getClass().getSimpleName(), receiver.getNodeAddress(),
disputeClosedMessage.getTradeId(), disputeClosedMessage.getUid(), disputeClosedMessage.getTradeId(), disputeClosedMessage.getUid(),
chatMessage.getUid()); disputeResult.getChatMessage().getUid());
// We use the chatMessage wrapped inside the DisputeClosedMessage for // We use the chatMessage wrapped inside the DisputeClosedMessage for
// the state, as that is displayed to the user and we only persist that msg // the state, as that is displayed to the user and we only persist that msg
chatMessage.setArrived(true); disputeResult.getChatMessage().setArrived(true);
trade.setDisputeStateIfProgress(Trade.DisputeState.ARBITRATOR_SAW_ARRIVED_DISPUTE_CLOSED_MSG); trade.setDisputeStateIfProgress(Trade.DisputeState.ARBITRATOR_SAW_ARRIVED_DISPUTE_CLOSED_MSG);
trade.syncWalletNormallyForMs(30000); trade.syncWalletNormallyForMs(30000);
requestPersistence(); requestPersistence();
@ -759,11 +761,11 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
"chatMessage.uid={}", "chatMessage.uid={}",
disputeClosedMessage.getClass().getSimpleName(), receiver.getNodeAddress(), disputeClosedMessage.getClass().getSimpleName(), receiver.getNodeAddress(),
disputeClosedMessage.getTradeId(), disputeClosedMessage.getUid(), disputeClosedMessage.getTradeId(), disputeClosedMessage.getUid(),
chatMessage.getUid()); disputeResult.getChatMessage().getUid());
// We use the chatMessage wrapped inside the DisputeClosedMessage for // We use the chatMessage wrapped inside the DisputeClosedMessage for
// the state, as that is displayed to the user and we only persist that msg // the state, as that is displayed to the user and we only persist that msg
chatMessage.setStoredInMailbox(true); disputeResult.getChatMessage().setStoredInMailbox(true);
Trade trade = tradeManager.getTrade(dispute.getTradeId()); Trade trade = tradeManager.getTrade(dispute.getTradeId());
trade.setDisputeStateIfProgress(Trade.DisputeState.ARBITRATOR_STORED_IN_MAILBOX_DISPUTE_CLOSED_MSG); trade.setDisputeStateIfProgress(Trade.DisputeState.ARBITRATOR_STORED_IN_MAILBOX_DISPUTE_CLOSED_MSG);
requestPersistence(); requestPersistence();
@ -776,11 +778,11 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
"chatMessage.uid={}, errorMessage={}", "chatMessage.uid={}, errorMessage={}",
disputeClosedMessage.getClass().getSimpleName(), receiver.getNodeAddress(), disputeClosedMessage.getClass().getSimpleName(), receiver.getNodeAddress(),
disputeClosedMessage.getTradeId(), disputeClosedMessage.getUid(), disputeClosedMessage.getTradeId(), disputeClosedMessage.getUid(),
chatMessage.getUid(), errorMessage); disputeResult.getChatMessage().getUid(), errorMessage);
// We use the chatMessage wrapped inside the DisputeClosedMessage for // We use the chatMessage wrapped inside the DisputeClosedMessage for
// the state, as that is displayed to the user and we only persist that msg // the state, as that is displayed to the user and we only persist that msg
chatMessage.setSendMessageError(errorMessage); disputeResult.getChatMessage().setSendMessageError(errorMessage);
trade.setDisputeStateIfProgress(Trade.DisputeState.ARBITRATOR_SEND_FAILED_DISPUTE_CLOSED_MSG); trade.setDisputeStateIfProgress(Trade.DisputeState.ARBITRATOR_SEND_FAILED_DISPUTE_CLOSED_MSG);
requestPersistence(); requestPersistence();
faultHandler.handleFault(errorMessage, new RuntimeException(errorMessage)); faultHandler.handleFault(errorMessage, new RuntimeException(errorMessage));
@ -914,7 +916,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
} }
private boolean isAgent(Dispute dispute) { private boolean isAgent(Dispute dispute) {
return pubKeyRing.equals(dispute.getAgentPubKeyRing()); return keyRing.getPubKeyRing().equals(dispute.getAgentPubKeyRing());
} }
private Optional<Dispute> findDispute(Dispute dispute) { private Optional<Dispute> findDispute(Dispute dispute) {
@ -987,7 +989,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
ChatMessage mediatorsDisputeClosedMessage = new ChatMessage( ChatMessage mediatorsDisputeClosedMessage = new ChatMessage(
getSupportType(), getSupportType(),
dispute.getTradeId(), dispute.getTradeId(),
pubKeyRing.hashCode(), keyRing.getPubKeyRing().hashCode(),
false, false,
mediatorsDisputeResult, mediatorsDisputeResult,
p2PService.getAddress()); p2PService.getAddress());
@ -1074,7 +1076,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
ChatMessage priceInfoMessage = new ChatMessage( ChatMessage priceInfoMessage = new ChatMessage(
getSupportType(), getSupportType(),
dispute.getTradeId(), dispute.getTradeId(),
pubKeyRing.hashCode(), keyRing.getPubKeyRing().hashCode(),
false, false,
priceInfoText, priceInfoText,
p2PService.getAddress()); p2PService.getAddress());

View file

@ -88,6 +88,7 @@ public class DisputeSummaryVerification {
throw new IllegalArgumentException(Res.get("support.sigCheck.popup.failed")); throw new IllegalArgumentException(Res.get("support.sigCheck.popup.failed"));
} }
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace();
throw new IllegalArgumentException(Res.get("support.sigCheck.popup.invalidFormat")); throw new IllegalArgumentException(Res.get("support.sigCheck.popup.invalidFormat"));
} }
} }

View file

@ -211,7 +211,7 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
Dispute dispute = disputeOptional.get(); Dispute dispute = disputeOptional.get();
// verify that arbitrator does not get DisputeClosedMessage // verify that arbitrator does not get DisputeClosedMessage
if (pubKeyRing.equals(dispute.getAgentPubKeyRing())) { if (keyRing.getPubKeyRing().equals(dispute.getAgentPubKeyRing())) {
log.error("Arbitrator received disputeResultMessage. That should never happen."); log.error("Arbitrator received disputeResultMessage. That should never happen.");
return; return;
} }
@ -225,8 +225,7 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
} }
dispute.setIsClosed(); dispute.setIsClosed();
if (dispute.disputeResultProperty().get() != null) { if (dispute.disputeResultProperty().get() != null) {
log.warn("We already got a dispute result. That should only happen if a dispute needs to be closed " + log.info("We already got a dispute result, indicating the message was resent after updating multisig info. TradeId = " + tradeId);
"again because the first close did not succeed. TradeId = " + tradeId);
} }
dispute.setDisputeResult(disputeResult); dispute.setDisputeResult(disputeResult);

View file

@ -41,6 +41,7 @@ import bisq.core.trade.protocol.tasks.TradeTask;
import bisq.core.trade.protocol.tasks.VerifyPeersAccountAgeWitness; import bisq.core.trade.protocol.tasks.VerifyPeersAccountAgeWitness;
import bisq.core.trade.protocol.FluentProtocol.Condition; import bisq.core.trade.protocol.FluentProtocol.Condition;
import bisq.core.trade.protocol.tasks.ApplyFilter; import bisq.core.trade.protocol.tasks.ApplyFilter;
import bisq.core.trade.protocol.tasks.ResendDisputeClosedMessageWithPayout;
import bisq.core.trade.protocol.tasks.MaybeSendSignContractRequest; import bisq.core.trade.protocol.tasks.MaybeSendSignContractRequest;
import bisq.core.trade.protocol.tasks.ProcessDepositResponse; import bisq.core.trade.protocol.tasks.ProcessDepositResponse;
import bisq.core.trade.protocol.tasks.ProcessDepositsConfirmedMessage; import bisq.core.trade.protocol.tasks.ProcessDepositsConfirmedMessage;
@ -73,6 +74,7 @@ import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.EasyBind;
@ -162,6 +164,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
// TODO (woodser): this method only necessary because isPubKeyValid not called with sender argument, so it's validated before // TODO (woodser): this method only necessary because isPubKeyValid not called with sender argument, so it's validated before
private void handleMailboxCollectionSkipValidation(Collection<DecryptedMessageWithPubKey> collection) { private void handleMailboxCollectionSkipValidation(Collection<DecryptedMessageWithPubKey> collection) {
log.warn("TradeProtocol.handleMailboxCollectionSkipValidation");
collection.stream() collection.stream()
.map(DecryptedMessageWithPubKey::getNetworkEnvelope) .map(DecryptedMessageWithPubKey::getNetworkEnvelope)
.filter(this::isMyMessage) .filter(this::isMyMessage)
@ -181,8 +184,9 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
.forEach(this::handleMailboxMessage); .forEach(this::handleMailboxMessage);
} }
private static class MailboxMessageComparator implements Comparator<MailboxMessage> { public static class MailboxMessageComparator implements Comparator<MailboxMessage> {
private static List<Class<? extends MailboxMessage>> messageOrder = Arrays.asList( private static List<Class<? extends MailboxMessage>> messageOrder = Arrays.asList(
AckMessage.class,
DepositsConfirmedMessage.class, DepositsConfirmedMessage.class,
PaymentSentMessage.class, PaymentSentMessage.class,
PaymentReceivedMessage.class, PaymentReceivedMessage.class,
@ -397,7 +401,8 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
.from(sender)) .from(sender))
.setup(tasks( .setup(tasks(
ProcessDepositsConfirmedMessage.class, ProcessDepositsConfirmedMessage.class,
VerifyPeersAccountAgeWitness.class) VerifyPeersAccountAgeWitness.class,
ResendDisputeClosedMessageWithPayout.class)
.using(new TradeTaskRunner(trade, .using(new TradeTaskRunner(trade,
() -> { () -> {
handleTaskRunnerSuccess(sender, response); handleTaskRunnerSuccess(sender, response);

View file

@ -0,0 +1,84 @@
/*
* This file is part of Haveno.
*
* Haveno is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Haveno is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.core.trade.protocol.tasks;
import bisq.common.taskrunner.TaskRunner;
import bisq.core.support.dispute.Dispute;
import bisq.core.trade.HavenoUtils;
import bisq.core.trade.Trade;
import bisq.core.trade.messages.DepositsConfirmedMessage;
import bisq.core.trade.protocol.TradingPeer;
import bisq.core.util.Validator;
import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.List;
@Slf4j
public class ResendDisputeClosedMessageWithPayout extends TradeTask {
@SuppressWarnings({"unused"})
public ResendDisputeClosedMessageWithPayout(TaskRunner taskHandler, Trade trade) {
super(taskHandler, trade);
}
@Override
protected void run() {
try {
runInterceptHook();
// get peer
DepositsConfirmedMessage request = (DepositsConfirmedMessage) processModel.getTradeMessage();
checkNotNull(request);
Validator.checkTradeId(processModel.getOfferId(), request);
TradingPeer sender = trade.getTradingPeer(request.getPubKeyRing());
if (sender == null) throw new RuntimeException("Pub key ring is not from arbitrator, buyer, or seller");
// arbitrator resends DisputeClosedMessage with payout tx when updated multisig info received
boolean ticketClosed = false;
if (!trade.isPayoutPublished() && trade.isArbitrator()) {
List<Dispute> disputes = trade.getDisputes();
for (Dispute dispute : disputes) {
if (!dispute.isClosed()) continue; // dispute must be closed
if (sender.getPubKeyRing().equals(dispute.getTraderPubKeyRing())) {
HavenoUtils.arbitrationManager.closeDisputeTicket(dispute.getDisputeResultProperty().get(), dispute, dispute.getDisputeResultProperty().get().summaryNotesProperty().get(), null, () -> {
completeAux();
}, (errMessage, err) -> {
err.printStackTrace();
failed(err);
});
ticketClosed = true;
break;
}
}
}
// complete if not waiting for result
if (!ticketClosed) completeAux();
} catch (Throwable t) {
failed(t);
}
}
private void completeAux() {
processModel.getTradeManager().requestPersistence();
complete();
}
}