arbitrator sends original unsigned payout tx if published

synchronize on trade when processing dispute messages
This commit is contained in:
woodser 2023-02-25 08:13:44 -05:00
parent 17ac09fa4d
commit 6dca11f471
5 changed files with 135 additions and 123 deletions

View File

@ -36,7 +36,6 @@ import java.util.List;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import monero.wallet.model.MoneroTxWallet;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.lang.String.format;
@ -171,25 +170,25 @@ public class CoreDisputesService {
applyPayoutAmountsToDisputeResult(payout, winningDispute, disputeResult, customWinnerAmount);
// create dispute payout tx
MoneroTxWallet disputePayoutTx = arbitrationManager.createDisputePayoutTx(trade, winningDispute.getContract(), disputeResult, false);
trade.getProcessModel().setUnsignedPayoutTx(arbitrationManager.createDisputePayoutTx(trade, winningDispute.getContract(), disputeResult, false));
// close winning dispute ticket
closeDisputeTicket(arbitrationManager, winningDispute, disputeResult, disputePayoutTx, () -> {
closeDisputeTicket(arbitrationManager, winningDispute, disputeResult, () -> {
arbitrationManager.requestPersistence();
}, (errMessage, err) -> {
throw new IllegalStateException(errMessage, err);
});
// close loser's dispute ticket
var peersDisputeOptional = arbitrationManager.getDisputesAsObservableList().stream()
var loserDisputeOptional = arbitrationManager.getDisputesAsObservableList().stream()
.filter(d -> tradeId.equals(d.getTradeId()) && winningDispute.getTraderId() != d.getTraderId())
.findFirst();
if (!peersDisputeOptional.isPresent()) throw new IllegalStateException("could not find peer dispute");
var peerDispute = peersDisputeOptional.get();
var peerDisputeResult = createDisputeResult(peerDispute, winner, reason, summaryNotes, closeDate);
peerDisputeResult.setBuyerPayoutAmount(disputeResult.getBuyerPayoutAmount());
peerDisputeResult.setSellerPayoutAmount(disputeResult.getSellerPayoutAmount());
closeDisputeTicket(arbitrationManager, peerDispute, peerDisputeResult, disputePayoutTx, () -> {
if (!loserDisputeOptional.isPresent()) throw new IllegalStateException("could not find peer dispute");
var loserDispute = loserDisputeOptional.get();
var loserDisputeResult = createDisputeResult(loserDispute, winner, reason, summaryNotes, closeDate);
loserDisputeResult.setBuyerPayoutAmount(disputeResult.getBuyerPayoutAmount());
loserDisputeResult.setSellerPayoutAmount(disputeResult.getSellerPayoutAmount());
closeDisputeTicket(arbitrationManager, loserDispute, loserDisputeResult, () -> {
arbitrationManager.requestPersistence();
}, (errMessage, err) -> {
throw new IllegalStateException(errMessage, err);
@ -248,7 +247,7 @@ public class CoreDisputesService {
}
}
public void closeDisputeTicket(DisputeManager disputeManager, Dispute dispute, DisputeResult disputeResult, MoneroTxWallet payoutTx, ResultHandler resultHandler, FaultHandler faultHandler) {
public void closeDisputeTicket(DisputeManager disputeManager, Dispute dispute, DisputeResult disputeResult, ResultHandler resultHandler, FaultHandler faultHandler) {
DisputeResult.Reason reason = disputeResult.getReason();
String role = Res.get("shared.arbitrator");
@ -279,7 +278,7 @@ public class CoreDisputesService {
String summaryText = DisputeSummaryVerification.signAndApply(disputeManager, disputeResult, textToSign);
summaryText += Res.get("disputeSummaryWindow.close.nextStepsForRefundAgentArbitration");
disputeManager.closeDisputeTicket(disputeResult, dispute, summaryText, payoutTx, () -> {
disputeManager.closeDisputeTicket(disputeResult, dispute, summaryText, () -> {
dispute.setDisputeResult(disputeResult);
dispute.setIsClosed();
resultHandler.handleResult();

View File

@ -443,7 +443,14 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
Dispute dispute = message.getDispute();
log.info("{}.onDisputeOpenedMessage() with trade {}, dispute {}", getClass().getSimpleName(), dispute.getTradeId(), dispute.getId());
Trade trade = null;
// get trade
Trade trade = tradeManager.getTrade(dispute.getTradeId());
if (trade == null) {
log.warn("Dispute trade {} does not exist", dispute.getTradeId());
return;
}
synchronized (trade) {
String errorMessage = null;
PubKeyRing senderPubKeyRing = null;
try {
@ -470,13 +477,6 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
throw e;
}
// get trade
trade = tradeManager.getTrade(dispute.getTradeId());
if (trade == null) {
log.warn("Dispute trade {} does not exist", dispute.getTradeId());
return;
}
// get sender
senderPubKeyRing = trade.isArbitrator() ? (dispute.isDisputeOpenerIsBuyer() ? contract.getBuyerPubKeyRing() : contract.getSellerPubKeyRing()) : trade.getArbitrator().getPubKeyRing();
TradePeer sender = trade.getTradePeer(senderPubKeyRing);
@ -543,6 +543,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
requestPersistence();
}
}
// arbitrator sends dispute opened message to opener's peer
private void sendDisputeOpenedMessageToPeer(Dispute disputeFromOpener,
@ -700,16 +701,13 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
}
// arbitrator sends result to trader when their dispute is closed
public void closeDisputeTicket(DisputeResult disputeResult, Dispute dispute, String summaryText, MoneroTxWallet payoutTx, ResultHandler resultHandler, FaultHandler faultHandler) {
public void closeDisputeTicket(DisputeResult disputeResult, Dispute dispute, String summaryText, ResultHandler resultHandler, FaultHandler faultHandler) {
try {
// get trade
Trade trade = tradeManager.getTrade(dispute.getTradeId());
if (trade == null) throw new RuntimeException("Dispute trade " + dispute.getTradeId() + " does not exist");
// create dispute payout tx if not given
if (payoutTx == null) payoutTx = createDisputePayoutTx(trade, dispute.getContract(), disputeResult, false); // can be null if already published or we don't have receiver's multisig hex
// persist result in dispute's chat message once
boolean resending = disputeResult.getChatMessage() != null;
if (!resending) {
@ -724,9 +722,15 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
dispute.addAndPersistChatMessage(chatMessage);
}
// create dispute closed message
// create dispute payout tx if not published
TradePeer receiver = trade.getTradePeer(dispute.getTraderPubKeyRing());
String unsignedPayoutTxHex = payoutTx == null ? null : payoutTx.getTxSet().getMultisigTxHex();
if (!trade.isPayoutPublished() && receiver.getUpdatedMultisigHex() != null) {
trade.getProcessModel().setUnsignedPayoutTx(createDisputePayoutTx(trade, dispute.getContract(), disputeResult, false)); // can be null if we don't have receiver's multisig hex
}
// create dispute closed message
MoneroTxWallet unsignedPayoutTx = receiver.getUpdatedMultisigHex() == null ? null : trade.getProcessModel().getUnsignedPayoutTx();
String unsignedPayoutTxHex = unsignedPayoutTx == null ? null : unsignedPayoutTx.getTxSet().getMultisigTxHex();
TradePeer receiverPeer = receiver == trade.getBuyer() ? trade.getSeller() : trade.getBuyer();
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,
@ -799,9 +803,9 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
);
// save state
if (payoutTx != null) {
trade.setPayoutTx(payoutTx);
trade.setPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex());
if (unsignedPayoutTx != null) {
trade.setPayoutTx(unsignedPayoutTx);
trade.setPayoutTxHex(unsignedPayoutTx.getTxSet().getMultisigTxHex());
}
trade.advanceDisputeState(Trade.DisputeState.ARBITRATOR_SENT_DISPUTE_CLOSED_MSG);
requestPersistence();

View File

@ -169,6 +169,9 @@ public class ProcessModel implements Model, PersistablePayload {
@Getter
@Setter
transient private MoneroTxWallet depositTxXmr;
@Getter
@Setter
transient private MoneroTxWallet unsignedPayoutTx;
@Nullable
@Getter
@Setter

View File

@ -58,7 +58,8 @@ public class ResendDisputeClosedMessageWithPayout extends TradeTask {
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, () -> {
log.info("Arbitrator resending DisputeClosedMessage for trade {} after receiving updated multisig hex", trade.getId());
HavenoUtils.arbitrationManager.closeDisputeTicket(dispute.getDisputeResultProperty().get(), dispute, dispute.getDisputeResultProperty().get().summaryNotesProperty().get(), () -> {
completeAux();
}, (errMessage, err) -> {
err.printStackTrace();

View File

@ -590,8 +590,13 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
closeTicketButton.setOnAction(e -> {
// get or create payout tx
MoneroTxWallet payoutTx = trade.isPayoutPublished() ? trade.getPayoutTx() : arbitrationManager.createDisputePayoutTx(trade, dispute.getContract(), disputeResult, false);
// get or create dispute payout tx
MoneroTxWallet payoutTx = null;
if (trade.isPayoutPublished()) payoutTx = trade.getPayoutTx();
else {
payoutTx = arbitrationManager.createDisputePayoutTx(trade, dispute.getContract(), disputeResult, false);
trade.getProcessModel().setUnsignedPayoutTx(payoutTx);
}
// show confirmation
if (dispute.getSupportType() == SupportType.ARBITRATION &&
@ -600,9 +605,9 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
showPayoutTxConfirmation(contract,
disputeResult,
payoutTx,
() -> doClose(closeTicketButton, payoutTx));
() -> doClose(closeTicketButton));
} else {
doClose(closeTicketButton, payoutTx);
doClose(closeTicketButton);
}
});
@ -654,7 +659,7 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
}
}
private void doClose(Button closeTicketButton, MoneroTxWallet payoutTx) {
private void doClose(Button closeTicketButton) {
DisputeManager<? extends DisputeList<Dispute>> disputeManager = getDisputeManager(dispute);
if (disputeManager == null) {
return;
@ -663,7 +668,7 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
summaryNotesTextArea.textProperty().unbindBidirectional(disputeResult.summaryNotesProperty());
disputeResult.setCloseDate(new Date());
disputesService.closeDisputeTicket(disputeManager, dispute, disputeResult, payoutTx, () -> {
disputesService.closeDisputeTicket(disputeManager, dispute, disputeResult, () -> {
if (peersDisputeOptional.isPresent() && !peersDisputeOptional.get().isClosed() && !DevEnv.isDevMode()) {
new Popup().attention(Res.get("disputeSummaryWindow.close.closePeer")).show();
}