Skip to content

Commit ce5087c

Browse files
committed
Add preliminary code to publish warning tx if mediation fails
Adapt the existing workflow of starting a second-round arbitration process, upon mediation failure, to the v5 trade protocol, by giving the trader an option to broadcast his warning tx. This replaces the current (tertiary) action of broadcasting the (v4 protocol) delayed payout tx to start arbitration, on the mediation result popup. Instead, the trader must now wait for the peer to see the warning tx and actually start arbitration by broadcasting his redirect tx. (This second part is not yet implemented.) Also clean up 'DisputeValidation' slightly and prevent the errant display of a duplicate-DPT-detected message in the event that a dispute has a missing delayed payout txId (as is currently the case for v5 protocol trades). Fix the logic similarly for missing trade IDs & deposit txIds. TODO: Allow peer to start arbitration by broadcasting his redirect tx, upon detection (via a suitable listener) of a warning tx broadcast.
1 parent cf4016e commit ce5087c

File tree

5 files changed

+116
-48
lines changed

5 files changed

+116
-48
lines changed

core/src/main/java/bisq/core/support/dispute/DisputeValidation.java

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -218,22 +218,16 @@ private static Tuple3<Map<String, Set<String>>, Map<String, Set<String>>, Map<St
218218
String uid = dispute.getUid();
219219

220220
String tradeId = dispute.getTradeId();
221-
disputesPerTradeId.putIfAbsent(tradeId, new HashSet<>());
222-
Set<String> set = disputesPerTradeId.get(tradeId);
223-
set.add(uid);
221+
disputesPerTradeId.computeIfAbsent(tradeId, id -> new HashSet<>()).add(uid);
224222

225223
String delayedPayoutTxId = dispute.getDelayedPayoutTxId();
226224
if (delayedPayoutTxId != null) {
227-
disputesPerDelayedPayoutTxId.putIfAbsent(delayedPayoutTxId, new HashSet<>());
228-
set = disputesPerDelayedPayoutTxId.get(delayedPayoutTxId);
229-
set.add(uid);
225+
disputesPerDelayedPayoutTxId.computeIfAbsent(delayedPayoutTxId, id -> new HashSet<>()).add(uid);
230226
}
231227

232228
String depositTxId = dispute.getDepositTxId();
233229
if (depositTxId != null) {
234-
disputesPerDepositTxId.putIfAbsent(depositTxId, new HashSet<>());
235-
set = disputesPerDepositTxId.get(depositTxId);
236-
set.add(uid);
230+
disputesPerDepositTxId.computeIfAbsent(depositTxId, id -> new HashSet<>()).add(uid);
237231
}
238232
});
239233

@@ -255,6 +249,7 @@ private static void testIfDisputeTriesReplay(Dispute disputeToTest,
255249
// So until all users have updated to 1.4.0 we only check in refund agent case. With 1.4.0 we send the
256250
// delayed payout tx also in mediation cases and that if check can be removed.
257251
if (disputeToTest.getSupportType() == SupportType.REFUND) {
252+
// TODO: Handle v5 protocol trades, which have no delayed payout tx.
258253
checkNotNull(disputeToTestDelayedPayoutTxId,
259254
"Delayed payout transaction ID is null. " +
260255
"Trade ID: " + disputeToTestTradeId);
@@ -265,20 +260,20 @@ private static void testIfDisputeTriesReplay(Dispute disputeToTest,
265260
"agentsUid must not be null. Trade ID: " + disputeToTestTradeId);
266261

267262
Set<String> disputesPerTradeIdItems = disputesPerTradeId.get(disputeToTestTradeId);
268-
checkArgument(disputesPerTradeIdItems != null && disputesPerTradeIdItems.size() <= 2,
269-
"We found more then 2 disputes with the same trade ID. " +
270-
"Trade ID: " + disputeToTestTradeId);
263+
checkArgument(disputesPerTradeIdItems == null || disputesPerTradeIdItems.size() <= 2,
264+
"We found more than 2 disputes with the same trade ID. " +
265+
"Trade ID: %s", disputeToTestTradeId);
271266
if (!disputesPerDelayedPayoutTxId.isEmpty()) {
272267
Set<String> disputesPerDelayedPayoutTxIdItems = disputesPerDelayedPayoutTxId.get(disputeToTestDelayedPayoutTxId);
273-
checkArgument(disputesPerDelayedPayoutTxIdItems != null && disputesPerDelayedPayoutTxIdItems.size() <= 2,
274-
"We found more then 2 disputes with the same delayedPayoutTxId. " +
275-
"Trade ID: " + disputeToTestTradeId);
268+
checkArgument(disputesPerDelayedPayoutTxIdItems == null || disputesPerDelayedPayoutTxIdItems.size() <= 2,
269+
"We found more than 2 disputes with the same delayedPayoutTxId. " +
270+
"Trade ID: %s", disputeToTestTradeId);
276271
}
277272
if (!disputesPerDepositTxId.isEmpty()) {
278273
Set<String> disputesPerDepositTxIdItems = disputesPerDepositTxId.get(disputeToTestDepositTxId);
279-
checkArgument(disputesPerDepositTxIdItems != null && disputesPerDepositTxIdItems.size() <= 2,
280-
"We found more then 2 disputes with the same depositTxId. " +
281-
"Trade ID: " + disputeToTestTradeId);
274+
checkArgument(disputesPerDepositTxIdItems == null || disputesPerDepositTxIdItems.size() <= 2,
275+
"We found more than 2 disputes with the same depositTxId. " +
276+
"Trade ID: %s", disputeToTestTradeId);
282277
}
283278
} catch (IllegalArgumentException e) {
284279
throw new DisputeReplayException(disputeToTest, e.getMessage());
@@ -287,7 +282,7 @@ private static void testIfDisputeTriesReplay(Dispute disputeToTest,
287282
"disputeToTest={}, disputesPerTradeId={}, disputesPerDelayedPayoutTxId={}, " +
288283
"disputesPerDepositTxId={}",
289284
disputeToTest, disputesPerTradeId, disputesPerDelayedPayoutTxId, disputesPerDepositTxId);
290-
throw new DisputeReplayException(disputeToTest, e.toString() + " at dispute " + disputeToTest.toString());
285+
throw new DisputeReplayException(disputeToTest, e + " at dispute " + disputeToTest);
291286
}
292287
}
293288

core/src/main/java/bisq/core/trade/protocol/bisq_v1/DisputeProtocol.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
import bisq.core.trade.protocol.bisq_v1.tasks.mediation.SendMediatedPayoutTxPublishedMessage;
4141
import bisq.core.trade.protocol.bisq_v1.tasks.mediation.SetupMediatedPayoutTxListener;
4242
import bisq.core.trade.protocol.bisq_v1.tasks.mediation.SignMediatedPayoutTx;
43+
import bisq.core.trade.protocol.bisq_v5.messages.DepositTxAndSellerPaymentAccountMessage;
44+
import bisq.core.trade.protocol.bisq_v5.tasks.arbitration.PublishWarningTx;
4345

4446
import bisq.network.p2p.AckMessage;
4547
import bisq.network.p2p.NodeAddress;
@@ -51,13 +53,13 @@
5153

5254
@Slf4j
5355
public class DisputeProtocol extends TradeProtocol {
54-
5556
protected Trade trade;
5657
protected final ProcessModel processModel;
5758

5859
enum DisputeEvent implements FluentProtocol.Event {
5960
MEDIATION_RESULT_ACCEPTED,
6061
MEDIATION_RESULT_REJECTED,
62+
WARNING_SENT,
6163
ARBITRATION_REQUESTED
6264
}
6365

@@ -84,7 +86,8 @@ protected void onAckMessage(AckMessage ackMessage, NodeAddress peer) {
8486
// as we support automatic re-send of the msg in case it was not ACKed after a certain time
8587
if (ackMessage.getSourceMsgClassName().equals(CounterCurrencyTransferStartedMessage.class.getSimpleName())) {
8688
processModel.setPaymentStartedAckMessage(ackMessage);
87-
} else if (ackMessage.getSourceMsgClassName().equals(DepositTxAndDelayedPayoutTxMessage.class.getSimpleName())) {
89+
} else if (ackMessage.getSourceMsgClassName().equals(DepositTxAndDelayedPayoutTxMessage.class.getSimpleName()) ||
90+
ackMessage.getSourceMsgClassName().equals(DepositTxAndSellerPaymentAccountMessage.class.getSimpleName())) {
8891
processModel.setDepositTxSentAckMessage(ackMessage);
8992
}
9093

@@ -206,6 +209,22 @@ public void onPublishDelayedPayoutTx(ResultHandler resultHandler, ErrorMessageHa
206209
}
207210

208211

212+
///////////////////////////////////////////////////////////////////////////////////////////
213+
// Warning tx
214+
///////////////////////////////////////////////////////////////////////////////////////////
215+
216+
public void onPublishWarningTx(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
217+
DisputeEvent event = DisputeEvent.WARNING_SENT;
218+
expect(anyPhase(Trade.Phase.DEPOSIT_CONFIRMED,
219+
Trade.Phase.FIAT_SENT,
220+
Trade.Phase.FIAT_RECEIVED)
221+
.with(event)
222+
.preCondition(trade.hasV5Protocol()))
223+
.setup(tasks(PublishWarningTx.class))
224+
.executeTasks();
225+
}
226+
227+
209228
///////////////////////////////////////////////////////////////////////////////////////////
210229
// Peer has published the delayed payout tx
211230
///////////////////////////////////////////////////////////////////////////////////////////

core/src/main/resources/i18n/displayStrings.properties

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,10 +1037,27 @@ portfolio.pending.mediationResult.popup.info=The mediator has suggested the foll
10371037
Please inform the mediator if the trade is not paid out in the next 48h.\n\n\
10381038
If agreement is not possible, you will have to wait until {2} (block {3}) to Send to Arbitration,\
10391039
which will open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\n\
1040-
If the trade goes to arbitration the arbitrator will pay out the trade amount plus one peer's security deposit. \
1040+
If the trade goes to arbitration the arbitrator will pay out the trade amount plus one peer''s security deposit. \
10411041
This means the total arbitration payout will be less than the mediation payout. \
10421042
Requesting arbitration is meant for exceptional circumstances. such as; \
1043-
one peer not responding, or disputing the mediator made a fair payout suggestion. \n\n\
1043+
one peer not responding, or disputing the mediator made a fair payout suggestion.\n\n\
1044+
More details about the arbitration model: [HYPERLINK:https://bisq.wiki/Dispute_resolution#Level_3:_Arbitration]
1045+
portfolio.pending.mediationResult.popup.info.v5=The mediator has suggested the following payout:\n\
1046+
You receive: {0}\n\
1047+
Your trading peer receives: {1}\n\n\
1048+
You can accept or reject this suggested payout.\n\n\
1049+
By accepting, you sign the proposed payout transaction. \
1050+
Mediation is expected to be the optimal resolution for both traders. \
1051+
If your trading peer also accepts and signs, the payout will be completed, and the trade will be closed. \
1052+
Please inform the mediator if the trade is not paid out in the next 48h.\n\n\
1053+
If agreement is not possible, you will have to wait until {2} (block {3}) to Send a Warning to your trading peer, \
1054+
by broadcasting your warning transaction. The peer will then have a further 10 days (1440 blocks) to respond, by \
1055+
opening a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their \
1056+
findings. If the peer does not respond in time, you can automatically claim the entire trade collateral.\n\n\
1057+
If the trade goes to arbitration the arbitrator will pay out the trade amount plus one peer''s security deposit. \
1058+
This means the total arbitration payout will be less than the mediation payout. \
1059+
Sending a warning or requesting arbitration is meant for exceptional circumstances. such as; \
1060+
one peer not responding, or disputing the mediator made a fair payout suggestion.\n\n\
10441061
More details about the arbitration model: [HYPERLINK:https://bisq.wiki/Dispute_resolution#Level_3:_Arbitration]
10451062
portfolio.pending.mediationResult.popup.selfAccepted.lockTimeOver=You have accepted the mediator''s suggested payout \
10461063
but it seems that your trading peer has not yet accepted it. \
@@ -1049,8 +1066,18 @@ portfolio.pending.mediationResult.popup.selfAccepted.lockTimeOver=You have accep
10491066
investigate the case again and do a payout based on their findings.\n\n\
10501067
You can find more details about the arbitration model at: \
10511068
[HYPERLINK:https://bisq.wiki/Dispute_resolution#Level_3:_Arbitration]
1069+
portfolio.pending.mediationResult.popup.selfAccepted.lockTimeOver.v5=You have accepted the mediator''s suggested payout \
1070+
but it seems that your trading peer has not yet accepted it. \
1071+
Inform your mediator that you have accepted mediation if your peer has not accepted the mediation suggestion in 48h.\n\n\
1072+
Once the lock time is over on {0} (block {1}), you can Send a Warning to your trading peer, by broadcasting your \
1073+
warning transaction. The peer will then have a further 10 days (1440 blocks) to respond, by opening a second-round \
1074+
dispute with an arbitrator who will investigate the case again and do a payout based on their findings. If the peer \
1075+
does not respond in time, you can automatically claim the entire trade collateral.\n\n\
1076+
You can find more details about the arbitration model at: \
1077+
[HYPERLINK:https://bisq.wiki/Dispute_resolution#Level_3:_Arbitration]
10521078
portfolio.pending.mediationResult.popup.reject=Reject
10531079
portfolio.pending.mediationResult.popup.openArbitration=Send to arbitration
1080+
portfolio.pending.mediationResult.popup.sendWarning=Send warning to peer
10541081
portfolio.pending.mediationResult.popup.alreadyAccepted=You've already accepted
10551082

10561083
portfolio.pending.failedTrade.taker.missingTakerFeeTx=The taker fee transaction is missing.\n\n\

desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,19 @@ public void onOpenDispute() {
253253
tryOpenDispute(false);
254254
}
255255

256+
public void onSendWarning() {
257+
Trade trade = getTrade();
258+
if (trade == null) {
259+
log.error("Trade is null");
260+
return;
261+
}
262+
// TODO: What if the peer has already broadcast his warning tx? We need to detect that.
263+
trade.setDisputeState(Trade.DisputeState.WARNING_SENT);
264+
((DisputeProtocol) tradeManager.getTradeProtocol(trade)).onPublishWarningTx(
265+
() -> log.info("Warning tx published"),
266+
errorMessage -> new Popup().error(errorMessage).show());
267+
}
268+
256269
public void onOpenSupportTicket() {
257270
tryOpenDispute(true);
258271
}

0 commit comments

Comments
 (0)