Skip to content

Commit

Permalink
Add code to redirect or claim after warning tx published
Browse files Browse the repository at this point in the history
Implement the redirect-to-arbitration & claim buttons in the trade step
view, allowing the warned peer to publish the redirect tx and open a
refund dispute, or the user to close the trade by publishing the claim
tx if an unresponsive peer. To this end, add '(warning|redirect)TxId'
fields to the 'Dispute' DTO and corresponding proto, to use in place of
the (now null) 'delayedPayoutTxId' field. The new fields allow the
refund agent to validate the tx chain in the case of the v5 protocol
(using the Mempool service to make sure the redirect tx is valid and
published/confirmed -- not yet implemented) and guard against replay
attacks. Update the validation logic accordingly (& add some TODOs).

Additionally, tidy up 'DisputeSummaryWindow' a little and fix a bug in
'SetupStagedTxListeners' preventing the redirect/claim tx listener from
firing after the warning tx appears, until an application restart. Also
ensure that it closes the trade instead of merely updating the dispute
state, when a claim tx is picked up.

TODO: Improve popup messages & fix arbtrator tx chain validation logic.
  • Loading branch information
stejbac committed Oct 1, 2024
1 parent 97f761b commit 4230a38
Show file tree
Hide file tree
Showing 14 changed files with 390 additions and 163 deletions.
47 changes: 26 additions & 21 deletions core/src/main/java/bisq/core/support/dispute/Dispute.java
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,14 @@ public static protobuf.Dispute.State toProtoMessage(Dispute.State state) {
@Setter
private long tradeTxFee;

// Added for v5 protocol
@Setter
@Nullable
private String warningTxId;
@Setter
@Nullable
private String redirectTxId;


// Should be only used in emergency case if we need to add data but do not want to break backward compatibility
// at the P2P network storage checks. The hash of the object will be used to verify if the data is valid. Any new
Expand Down Expand Up @@ -282,12 +290,14 @@ public protobuf.Dispute toProtoMessage() {
Optional.ofNullable(disputePayoutTxId).ifPresent(builder::setDisputePayoutTxId);
Optional.ofNullable(makerContractSignature).ifPresent(builder::setMakerContractSignature);
Optional.ofNullable(takerContractSignature).ifPresent(builder::setTakerContractSignature);
Optional.ofNullable(disputeResultProperty.get()).ifPresent(result -> builder.setDisputeResult(disputeResultProperty.get().toProtoMessage()));
Optional.ofNullable(supportType).ifPresent(result -> builder.setSupportType(SupportType.toProtoMessage(supportType)));
Optional.ofNullable(mediatorsDisputeResult).ifPresent(result -> builder.setMediatorsDisputeResult(mediatorsDisputeResult));
Optional.ofNullable(delayedPayoutTxId).ifPresent(result -> builder.setDelayedPayoutTxId(delayedPayoutTxId));
Optional.ofNullable(donationAddressOfDelayedPayoutTx).ifPresent(result -> builder.setDonationAddressOfDelayedPayoutTx(donationAddressOfDelayedPayoutTx));
Optional.ofNullable(disputeResultProperty.get()).ifPresent(e -> builder.setDisputeResult(e.toProtoMessage()));
Optional.ofNullable(supportType).ifPresent(e -> builder.setSupportType(SupportType.toProtoMessage(e)));
Optional.ofNullable(mediatorsDisputeResult).ifPresent(builder::setMediatorsDisputeResult);
Optional.ofNullable(delayedPayoutTxId).ifPresent(builder::setDelayedPayoutTxId);
Optional.ofNullable(donationAddressOfDelayedPayoutTx).ifPresent(builder::setDonationAddressOfDelayedPayoutTx);
Optional.ofNullable(getExtraDataMap()).ifPresent(builder::putAllExtraData);
Optional.ofNullable(warningTxId).ifPresent(builder::setWarningTxId);
Optional.ofNullable(redirectTxId).ifPresent(builder::setRedirectTxId);
return builder.build();
}

Expand Down Expand Up @@ -320,28 +330,21 @@ public static Dispute fromProto(protobuf.Dispute proto, CoreProtoResolver corePr
.map(ChatMessage::fromPayloadProto)
.collect(Collectors.toList()));

if (proto.hasDisputeResult())
if (proto.hasDisputeResult()) {
dispute.disputeResultProperty.set(DisputeResult.fromProto(proto.getDisputeResult()));
dispute.disputePayoutTxId = ProtoUtil.stringOrNullFromProto(proto.getDisputePayoutTxId());

String mediatorsDisputeResult = proto.getMediatorsDisputeResult();
if (!mediatorsDisputeResult.isEmpty()) {
dispute.setMediatorsDisputeResult(mediatorsDisputeResult);
}

String delayedPayoutTxId = proto.getDelayedPayoutTxId();
if (!delayedPayoutTxId.isEmpty()) {
dispute.setDelayedPayoutTxId(delayedPayoutTxId);
}
dispute.disputePayoutTxId = ProtoUtil.stringOrNullFromProto(proto.getDisputePayoutTxId());

String donationAddressOfDelayedPayoutTx = proto.getDonationAddressOfDelayedPayoutTx();
if (!donationAddressOfDelayedPayoutTx.isEmpty()) {
dispute.setDonationAddressOfDelayedPayoutTx(donationAddressOfDelayedPayoutTx);
}
dispute.setMediatorsDisputeResult(ProtoUtil.stringOrNullFromProto(proto.getMediatorsDisputeResult()));
dispute.setDelayedPayoutTxId(ProtoUtil.stringOrNullFromProto(proto.getDelayedPayoutTxId()));
dispute.setDonationAddressOfDelayedPayoutTx(ProtoUtil.stringOrNullFromProto(proto.getDonationAddressOfDelayedPayoutTx()));

dispute.setBurningManSelectionHeight(proto.getBurningManSelectionHeight());
dispute.setTradeTxFee(proto.getTradeTxFee());

dispute.setWarningTxId(ProtoUtil.stringOrNullFromProto(proto.getWarningTxId()));
dispute.setRedirectTxId(ProtoUtil.stringOrNullFromProto(proto.getRedirectTxId()));

if (Dispute.State.fromProto(proto.getState()) == State.NEEDS_UPGRADE) {
// old disputes did not have a state field, so choose an appropriate state:
dispute.setState(proto.getIsClosed() ? State.CLOSED : State.OPEN);
Expand Down Expand Up @@ -387,7 +390,7 @@ public void maybeClearSensitiveData() {
if (contract.maybeClearSensitiveData()) {
change += "contract;";
}
String edited = contract.sanitizeContractAsJson(contractAsJson);
String edited = Contract.sanitizeContractAsJson(contractAsJson);
if (!edited.equals(contractAsJson)) {
contractAsJson = edited;
change += "contractAsJson;";
Expand Down Expand Up @@ -571,6 +574,8 @@ public String toString() {
",\n cachedDepositTx='" + cachedDepositTx + '\'' +
",\n burningManSelectionHeight='" + burningManSelectionHeight + '\'' +
",\n tradeTxFee='" + tradeTxFee + '\'' +
",\n warningTxId='" + warningTxId + '\'' +
",\n redirectTxId='" + redirectTxId + '\'' +
"\n}";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ public void maybeClearSensitiveData() {
log.info("{} checking closed disputes eligibility for having sensitive data cleared", super.getClass().getSimpleName());
Instant safeDate = closedTradableManager.getSafeDateForSensitiveDataClearing();
getDisputeList().getList().stream()
.filter(e -> e.isClosed())
.filter(Dispute::isClosed)
.filter(e -> e.getOpeningDate().toInstant().isBefore(safeDate))
.forEach(Dispute::maybeClearSensitiveData);
requestPersistence();
Expand Down Expand Up @@ -491,7 +491,7 @@ private void peerOpenedDisputeForTrade(PeerOpenedDisputeMessage peerOpenedDisput
try {
DisputeValidation.validateDisputeData(dispute, btcWalletService);
DisputeValidation.validateNodeAddresses(dispute, config);
DisputeValidation.validateTradeAndDispute(dispute, trade);
DisputeValidation.validateTradeAndDispute(dispute, trade, btcWalletService);
TradeDataValidation.validateDelayedPayoutTx(trade,
trade.getDelayedPayoutTx(),
btcWalletService);
Expand Down Expand Up @@ -713,6 +713,8 @@ private void doSendPeerOpenedDisputeMessage(Dispute disputeFromOpener,
dispute.setDonationAddressOfDelayedPayoutTx(disputeFromOpener.getDonationAddressOfDelayedPayoutTx());
dispute.setBurningManSelectionHeight(disputeFromOpener.getBurningManSelectionHeight());
dispute.setTradeTxFee(disputeFromOpener.getTradeTxFee());
dispute.setWarningTxId(disputeFromOpener.getWarningTxId());
dispute.setRedirectTxId(disputeFromOpener.getRedirectTxId());

Optional<Dispute> storedDisputeOptional = findDispute(dispute);

Expand Down
Loading

0 comments on commit 4230a38

Please sign in to comment.