diff --git a/core/src/main/java/bisq/core/dao/DaoFacade.java b/core/src/main/java/bisq/core/dao/DaoFacade.java index 1485ae25465..0632b647d1e 100644 --- a/core/src/main/java/bisq/core/dao/DaoFacade.java +++ b/core/src/main/java/bisq/core/dao/DaoFacade.java @@ -95,6 +95,7 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; @@ -700,6 +701,14 @@ public String getParamValue(Param param) { return getParamValue(param, periodService.getChainHeight()); } + public Set getAllPastParamValues(Param param) { + Set set = new HashSet<>(); + periodService.getCycles().forEach(cycle -> { + set.add(getParamValue(param, cycle.getHeightOfFirstBlock())); + }); + return set; + } + public String getParamValue(Param param, int blockHeight) { return daoStateService.getParamValue(param, blockHeight); } diff --git a/core/src/main/java/bisq/core/support/dispute/Dispute.java b/core/src/main/java/bisq/core/support/dispute/Dispute.java index d5f45a7a185..d164ca81913 100644 --- a/core/src/main/java/bisq/core/support/dispute/Dispute.java +++ b/core/src/main/java/bisq/core/support/dispute/Dispute.java @@ -102,6 +102,9 @@ public final class Dispute implements NetworkPayload { @Setter @Nullable private String delayedPayoutTxId; + @Setter + @Nullable + private String donationAddressOfDelayedPayoutTx; /////////////////////////////////////////////////////////////////////////////////////////// @@ -228,6 +231,7 @@ public protobuf.Dispute 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)); return builder.build(); } @@ -271,6 +275,8 @@ public static Dispute fromProto(protobuf.Dispute proto, CoreProtoResolver corePr dispute.setDelayedPayoutTxId(delayedPayoutTxId); } + dispute.setDonationAddressOfDelayedPayoutTx(ProtoUtil.stringOrNullFromProto(proto.getDonationAddressOfDelayedPayoutTx())); + return dispute; } @@ -382,6 +388,7 @@ public String toString() { ",\n supportType=" + supportType + ",\n mediatorsDisputeResult='" + mediatorsDisputeResult + '\'' + ",\n delayedPayoutTxId='" + delayedPayoutTxId + '\'' + + ",\n donationAddressOfDelayedPayoutTx='" + donationAddressOfDelayedPayoutTx + '\'' + "\n}"; } } diff --git a/core/src/main/java/bisq/core/support/dispute/DisputeManager.java b/core/src/main/java/bisq/core/support/dispute/DisputeManager.java index 6a4036a18aa..3a4b6182fff 100644 --- a/core/src/main/java/bisq/core/support/dispute/DisputeManager.java +++ b/core/src/main/java/bisq/core/support/dispute/DisputeManager.java @@ -20,6 +20,8 @@ import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.TradeWalletService; +import bisq.core.dao.DaoFacade; +import bisq.core.dao.governance.param.Param; import bisq.core.locale.Res; import bisq.core.offer.OpenOfferManager; import bisq.core.support.SupportManager; @@ -44,19 +46,28 @@ import bisq.common.storage.Storage; import bisq.common.util.Tuple2; +import org.bitcoinj.core.Address; +import org.bitcoinj.core.NetworkParameters; +import org.bitcoinj.core.Transaction; +import org.bitcoinj.core.TransactionOutput; + import javafx.beans.property.IntegerProperty; +import javafx.collections.FXCollections; import javafx.collections.ObservableList; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import javax.annotation.Nullable; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; @Slf4j @@ -68,6 +79,9 @@ public abstract class DisputeManager disputeListService; + private final DaoFacade daoFacade; + @Getter + protected final ObservableList disputesWithInvalidDonationAddress = FXCollections.observableArrayList(); /////////////////////////////////////////////////////////////////////////////////////////// @@ -82,7 +96,8 @@ public DisputeManager(P2PService p2PService, ClosedTradableManager closedTradableManager, OpenOfferManager openOfferManager, PubKeyRing pubKeyRing, - DisputeListService disputeListService) { + DisputeListService disputeListService, + DaoFacade daoFacade) { super(p2PService, walletsSetup); this.tradeWalletService = tradeWalletService; @@ -92,6 +107,7 @@ public DisputeManager(P2PService p2PService, this.openOfferManager = openOfferManager; this.pubKeyRing = pubKeyRing; this.disputeListService = disputeListService; + this.daoFacade = daoFacade; } @@ -241,6 +257,30 @@ public Optional findOwnDispute(String tradeId) { return disputeList.stream().filter(e -> e.getTradeId().equals(tradeId)).findAny(); } + public boolean isValidDonationAddress(Dispute dispute) { + String addressAsString = dispute.getDonationAddressOfDelayedPayoutTx(); + + // We don't check in case of old clients which do not have the field set. + if (addressAsString == null) { + return true; + } + + // We use all past addresses from DAO param changes as the dispute case might have been opened later and the + // DAO param changed in the meantime. + Set allPastParamValues = daoFacade.getAllPastParamValues(Param.RECIPIENT_BTC_ADDRESS); + + if (allPastParamValues.contains(addressAsString)) { + return true; + } + + log.warn("Donation address is not a valid DAO donation address." + + "\nAddress used in the dispute: " + addressAsString + + "\nAll DAO param donation addresses:" + allPastParamValues); + return false; + + } + + /////////////////////////////////////////////////////////////////////////////////////////// // Message handler /////////////////////////////////////////////////////////////////////////////////////////// @@ -261,6 +301,11 @@ protected void onOpenNewDisputeMessage(OpenNewDisputeMessage openNewDisputeMessa dispute.setStorage(disputeListService.getStorage()); Contract contractFromOpener = dispute.getContract(); + + if (!isValidDonationAddress(dispute)) { + disputesWithInvalidDonationAddress.add(dispute); + } + PubKeyRing peersPubKeyRing = dispute.isDisputeOpenerIsBuyer() ? contractFromOpener.getSellerPubKeyRing() : contractFromOpener.getBuyerPubKeyRing(); if (isAgent(dispute)) { if (!disputeList.contains(dispute)) { @@ -315,6 +360,25 @@ protected void onPeerOpenedDisputeMessage(PeerOpenedDisputeMessage peerOpenedDis String errorMessage = null; Dispute dispute = peerOpenedDisputeMessage.getDispute(); + + Optional optionalTrade = tradeManager.getTradeById(dispute.getTradeId()); + checkArgument(optionalTrade.isPresent()); + Transaction delayedPayoutTx = optionalTrade.get().getDelayedPayoutTx(); + checkNotNull(delayedPayoutTx, "delayedPayoutTx must not be null"); + checkArgument(!delayedPayoutTx.getOutputs().isEmpty(), "delayedPayoutTx.getOutputs() must not be empty"); + NetworkParameters params = btcWalletService.getParams(); + TransactionOutput output = delayedPayoutTx.getOutput(0); + Address address = output.getAddressFromP2PKHScript(params); + if (address == null) { + address = output.getAddressFromP2SH(params); + } + checkNotNull(address, "address must not be null"); + String donationAddressOfDelayedPayoutTx = dispute.getDonationAddressOfDelayedPayoutTx(); + if (donationAddressOfDelayedPayoutTx != null) { + checkArgument(address.toString().equals(donationAddressOfDelayedPayoutTx), + "donationAddressOfDelayedPayoutTx from peers dispute must match address used in delayedPayoutTx"); + } + if (!isAgent(dispute)) { if (!disputeList.contains(dispute)) { Optional storedDisputeOptional = findDispute(dispute); @@ -498,6 +562,7 @@ private String sendPeerOpenedDisputeMessage(Dispute disputeFromOpener, disputeFromOpener.isSupportTicket(), disputeFromOpener.getSupportType()); dispute.setDelayedPayoutTxId(disputeFromOpener.getDelayedPayoutTxId()); + dispute.setDonationAddressOfDelayedPayoutTx(disputeFromOpener.getDonationAddressOfDelayedPayoutTx()); Optional storedDisputeOptional = findDispute(dispute); if (!storedDisputeOptional.isPresent()) { diff --git a/core/src/main/java/bisq/core/support/dispute/arbitration/ArbitrationManager.java b/core/src/main/java/bisq/core/support/dispute/arbitration/ArbitrationManager.java index e063e1c8528..16dd935e3ef 100644 --- a/core/src/main/java/bisq/core/support/dispute/arbitration/ArbitrationManager.java +++ b/core/src/main/java/bisq/core/support/dispute/arbitration/ArbitrationManager.java @@ -25,6 +25,7 @@ import bisq.core.btc.wallet.TradeWalletService; import bisq.core.btc.wallet.TxBroadcaster; import bisq.core.btc.wallet.WalletService; +import bisq.core.dao.DaoFacade; import bisq.core.locale.Res; import bisq.core.offer.OpenOffer; import bisq.core.offer.OpenOfferManager; @@ -88,9 +89,10 @@ public ArbitrationManager(P2PService p2PService, ClosedTradableManager closedTradableManager, OpenOfferManager openOfferManager, PubKeyRing pubKeyRing, - ArbitrationDisputeListService arbitrationDisputeListService) { + ArbitrationDisputeListService arbitrationDisputeListService, + DaoFacade daoFacade) { super(p2PService, tradeWalletService, walletService, walletsSetup, tradeManager, closedTradableManager, - openOfferManager, pubKeyRing, arbitrationDisputeListService); + openOfferManager, pubKeyRing, arbitrationDisputeListService, daoFacade); } diff --git a/core/src/main/java/bisq/core/support/dispute/mediation/MediationManager.java b/core/src/main/java/bisq/core/support/dispute/mediation/MediationManager.java index ea6b12bd997..85ffac66b57 100644 --- a/core/src/main/java/bisq/core/support/dispute/mediation/MediationManager.java +++ b/core/src/main/java/bisq/core/support/dispute/mediation/MediationManager.java @@ -20,6 +20,7 @@ import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.TradeWalletService; +import bisq.core.dao.DaoFacade; import bisq.core.locale.Res; import bisq.core.offer.OpenOffer; import bisq.core.offer.OpenOfferManager; @@ -80,9 +81,10 @@ public MediationManager(P2PService p2PService, ClosedTradableManager closedTradableManager, OpenOfferManager openOfferManager, PubKeyRing pubKeyRing, - MediationDisputeListService mediationDisputeListService) { + MediationDisputeListService mediationDisputeListService, + DaoFacade daoFacade) { super(p2PService, tradeWalletService, walletService, walletsSetup, tradeManager, closedTradableManager, - openOfferManager, pubKeyRing, mediationDisputeListService); + openOfferManager, pubKeyRing, mediationDisputeListService, daoFacade); } /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/main/java/bisq/core/support/dispute/refund/RefundManager.java b/core/src/main/java/bisq/core/support/dispute/refund/RefundManager.java index 139832a1c16..e978f20fa01 100644 --- a/core/src/main/java/bisq/core/support/dispute/refund/RefundManager.java +++ b/core/src/main/java/bisq/core/support/dispute/refund/RefundManager.java @@ -20,6 +20,7 @@ import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.TradeWalletService; +import bisq.core.dao.DaoFacade; import bisq.core.locale.Res; import bisq.core.offer.OpenOffer; import bisq.core.offer.OpenOfferManager; @@ -74,9 +75,10 @@ public RefundManager(P2PService p2PService, ClosedTradableManager closedTradableManager, OpenOfferManager openOfferManager, PubKeyRing pubKeyRing, - RefundDisputeListService refundDisputeListService) { + RefundDisputeListService refundDisputeListService, + DaoFacade daoFacade) { super(p2PService, tradeWalletService, walletService, walletsSetup, tradeManager, closedTradableManager, - openOfferManager, pubKeyRing, refundDisputeListService); + openOfferManager, pubKeyRing, refundDisputeListService, daoFacade); } /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index a6a1154e785..91067ccba3e 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -214,7 +214,8 @@ shared.mediator=Mediator shared.arbitrator=Arbitrator shared.refundAgent=Arbitrator shared.refundAgentForSupportStaff=Refund agent -shared.delayedPayoutTxId=Refund collateral transaction ID +shared.delayedPayoutTxId=Delayed payout transaction ID +shared.donationAddressOfDelayedPayoutTx=Donation address of delayed payout transaction shared.unconfirmedTransactionsLimitReached=You have too many unconfirmed transactions at the moment. Please try again later. @@ -1037,6 +1038,12 @@ support.peerOpenedDispute=Your trading peer has requested a dispute.\n\n{0}\n\nB support.peerOpenedDisputeForMediation=Your trading peer has requested mediation.\n\n{0}\n\nBisq version: {1} support.mediatorsDisputeSummary=System message:\nMediator''s dispute summary:\n{0} support.mediatorsAddress=Mediator''s node address: {0} +support.warning.disputesWithInvalidDonationAddress=The donation address used in the trade for this dispute is invalid \ + as it does not match any of the DAO parameter values for the donation address.\n\nThis might be a scam attempt. \ + Please inform the developers about that incident and do not close that case before the situation is resolved!\n\n\ + Address used in the dispute: {0}\n\n\ + All DAO param donation addresses: {1}\n\n\ + Trade ID: {2} #################################################################### diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/ContractWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/ContractWindow.java index ff053d90338..aa9c87a586a 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/ContractWindow.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/ContractWindow.java @@ -141,6 +141,8 @@ private void addContent() { rows++; if (dispute.getDelayedPayoutTxId() != null) rows++; + if (dispute.getDonationAddressOfDelayedPayoutTx() != null) + rows++; if (showAcceptedCountryCodes) rows++; if (showAcceptedBanks) @@ -248,6 +250,10 @@ private void addContent() { if (dispute.getDelayedPayoutTxId() != null) addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.delayedPayoutTxId"), dispute.getDelayedPayoutTxId()); + if (dispute.getDonationAddressOfDelayedPayoutTx() != null) + addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.donationAddressOfDelayedPayoutTx"), + dispute.getDonationAddressOfDelayedPayoutTx()); + if (dispute.getPayoutTxSerialized() != null) addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.payoutTxId"), dispute.getPayoutTxId()); diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/DisputeSummaryWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/DisputeSummaryWindow.java index 03374fd3938..96879b1499a 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/DisputeSummaryWindow.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/DisputeSummaryWindow.java @@ -34,6 +34,8 @@ import bisq.core.btc.wallet.Restrictions; import bisq.core.btc.wallet.TradeWalletService; import bisq.core.btc.wallet.TxBroadcaster; +import bisq.core.dao.DaoFacade; +import bisq.core.dao.governance.param.Param; import bisq.core.locale.Res; import bisq.core.offer.Offer; import bisq.core.provider.fee.FeeService; @@ -84,6 +86,7 @@ import java.util.Date; import java.util.Optional; +import java.util.Set; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; @@ -105,6 +108,7 @@ public class DisputeSummaryWindow extends Overlay { private final BtcWalletService btcWalletService; private final TxFeeEstimationService txFeeEstimationService; private final FeeService feeService; + private final DaoFacade daoFacade; private Dispute dispute; private Optional finalizeDisputeHandlerOptional = Optional.empty(); private ToggleGroup tradeAmountToggleGroup, reasonToggleGroup; @@ -138,7 +142,8 @@ public DisputeSummaryWindow(@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormat TradeWalletService tradeWalletService, BtcWalletService btcWalletService, TxFeeEstimationService txFeeEstimationService, - FeeService feeService) { + FeeService feeService, + DaoFacade daoFacade) { this.formatter = formatter; this.mediationManager = mediationManager; @@ -147,6 +152,7 @@ public DisputeSummaryWindow(@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormat this.btcWalletService = btcWalletService; this.txFeeEstimationService = txFeeEstimationService; this.feeService = feeService; + this.daoFacade = daoFacade; type = Type.Confirmation; } @@ -588,15 +594,27 @@ private void addButtons(Contract contract) { log.warn("dispute.getDepositTxSerialized is null"); return; } - if (dispute.getSupportType() == SupportType.REFUND && - peersDisputeOptional.isPresent() && - !peersDisputeOptional.get().isClosed()) { - showPayoutTxConfirmation(contract, disputeResult, - () -> { - doClose(closeTicketButton); - }); + + DisputeManager> disputeManager = getDisputeManager(dispute); + checkNotNull(disputeManager); + + if (disputeManager.isValidDonationAddress(dispute)) { + if (dispute.getSupportType() == SupportType.REFUND && + peersDisputeOptional.isPresent() && + !peersDisputeOptional.get().isClosed()) { + showPayoutTxConfirmation(contract, disputeResult, + () -> { + doClose(closeTicketButton); + }); + } else { + doClose(closeTicketButton); + } } else { - doClose(closeTicketButton); + String addressAsString = dispute.getDonationAddressOfDelayedPayoutTx(); + Set allPastParamValues = daoFacade.getAllPastParamValues(Param.RECIPIENT_BTC_ADDRESS); + String tradeId = dispute.getTradeId(); + new Popup().warning(Res.get("support.warning.disputesWithInvalidDonationAddress", + addressAsString, allPastParamValues, tradeId)).show(); } }); @@ -687,6 +705,22 @@ public void onFailure(TxBroadcastException exception) { } private void doClose(Button closeTicketButton) { + DisputeManager> disputeManager = getDisputeManager(dispute); + checkNotNull(disputeManager); + if (!disputeManager.isValidDonationAddress(dispute)) { + String addressAsString = dispute.getDonationAddressOfDelayedPayoutTx(); + Set allPastParamValues = daoFacade.getAllPastParamValues(Param.RECIPIENT_BTC_ADDRESS); + String tradeId = dispute.getTradeId(); + new Popup().warning(Res.get("support.warning.disputesWithInvalidDonationAddress", + addressAsString, allPastParamValues, tradeId)).show(); + + // For mediators we do not enforce that the case cannot be closed to stay flexible, + // but for refund agents we do. + if (disputeManager instanceof RefundManager) { + return; + } + } + disputeResult.setLoserPublisher(isLoserPublisherCheckBox.isSelected()); disputeResult.setCloseDate(new Date()); dispute.setDisputeResult(disputeResult); diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java index 9f5276f9b97..eadd5b6b1d2 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java @@ -63,9 +63,12 @@ import bisq.common.handlers.FaultHandler; import bisq.common.handlers.ResultHandler; +import org.bitcoinj.core.Address; import org.bitcoinj.core.Coin; +import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.core.Transaction; import org.bitcoinj.core.TransactionConfidence; +import org.bitcoinj.core.TransactionOutput; import com.google.inject.Inject; @@ -538,6 +541,18 @@ private void doOpenDispute(boolean isSupportTicket, Transaction depositTx) { // In case we re-open a dispute we allow Trade.DisputeState.REFUND_REQUESTED useRefundAgent = disputeState == Trade.DisputeState.MEDIATION_CLOSED || disputeState == Trade.DisputeState.REFUND_REQUESTED; + Transaction delayedPayoutTx = trade.getDelayedPayoutTx(); + checkNotNull(delayedPayoutTx, "delayedPayoutTx must not be null"); + checkArgument(!delayedPayoutTx.getOutputs().isEmpty(), "delayedPayoutTx.getOutputs() must not be empty"); + TransactionOutput output = delayedPayoutTx.getOutput(0); + NetworkParameters params = btcWalletService.getParams(); + Address donationAddressOfDelayedPayoutTx = output.getAddressFromP2PKHScript(params); + if (donationAddressOfDelayedPayoutTx == null) { + // The donation address can be as well be a multisig address. + donationAddressOfDelayedPayoutTx = output.getAddressFromP2SH(params); + checkNotNull(donationAddressOfDelayedPayoutTx, "address must not be null"); + } + ResultHandler resultHandler; if (useMediation) { // If no dispute state set we start with mediation @@ -566,6 +581,8 @@ private void doOpenDispute(boolean isSupportTicket, Transaction depositTx) { isSupportTicket, SupportType.MEDIATION); + dispute.setDonationAddressOfDelayedPayoutTx(donationAddressOfDelayedPayoutTx.toString()); + trade.setDisputeState(Trade.DisputeState.MEDIATION_REQUESTED); disputeManager.sendOpenNewDisputeMessage(dispute, false, @@ -641,6 +658,8 @@ private void doOpenDispute(boolean isSupportTicket, Transaction depositTx) { isSupportTicket, SupportType.REFUND); + dispute.setDonationAddressOfDelayedPayoutTx(donationAddressOfDelayedPayoutTx.toString()); + String tradeId = dispute.getTradeId(); mediationManager.findDispute(tradeId) .ifPresent(mediatorsDispute -> { @@ -654,7 +673,7 @@ private void doOpenDispute(boolean isSupportTicket, Transaction depositTx) { } }); - dispute.setDelayedPayoutTxId(trade.getDelayedPayoutTx().getHashAsString()); + dispute.setDelayedPayoutTxId(delayedPayoutTx.getHashAsString()); trade.setDisputeState(Trade.DisputeState.REFUND_REQUESTED); diff --git a/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/DisputeAgentView.java b/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/DisputeAgentView.java index 864e97148f8..7fdf3db404b 100644 --- a/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/DisputeAgentView.java +++ b/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/DisputeAgentView.java @@ -18,6 +18,7 @@ package bisq.desktop.main.support.dispute.agent; import bisq.desktop.components.AutoTooltipButton; +import bisq.desktop.main.overlays.popups.Popup; import bisq.desktop.main.overlays.windows.ContractWindow; import bisq.desktop.main.overlays.windows.DisputeSummaryWindow; import bisq.desktop.main.overlays.windows.TradeDetailsWindow; @@ -26,6 +27,8 @@ import bisq.core.account.witness.AccountAgeWitnessService; import bisq.core.alert.PrivateNotificationManager; +import bisq.core.dao.DaoFacade; +import bisq.core.dao.governance.param.Param; import bisq.core.locale.Res; import bisq.core.support.dispute.Dispute; import bisq.core.support.dispute.DisputeList; @@ -38,8 +41,15 @@ import javafx.scene.control.Button; +import javafx.collections.ListChangeListener; + +import java.util.Set; + public abstract class DisputeAgentView extends DisputeView { + private final DaoFacade daoFacade; + private ListChangeListener disputesWithInvalidDonationAddressListener; + public DisputeAgentView(DisputeManager> disputeManager, KeyRing keyRing, TradeManager tradeManager, @@ -49,6 +59,7 @@ public DisputeAgentView(DisputeManager { + c.next(); + if (c.wasAdded()) { + c.getAddedSubList().forEach(dispute -> { + String addressAsString = dispute.getDonationAddressOfDelayedPayoutTx(); + Set allPastParamValues = daoFacade.getAllPastParamValues(Param.RECIPIENT_BTC_ADDRESS); + String tradeId = dispute.getTradeId(); + new Popup().warning(Res.get("support.warning.disputesWithInvalidDonationAddress", + addressAsString, allPastParamValues, tradeId)).show(); + }); + } + }; + } + + @Override + protected void activate() { + super.activate(); + disputeManager.getDisputesWithInvalidDonationAddress().addListener(disputesWithInvalidDonationAddressListener); + } + + @Override + protected void deactivate() { + super.deactivate(); + disputeManager.getDisputesWithInvalidDonationAddress().removeListener(disputesWithInvalidDonationAddressListener); } @Override diff --git a/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/arbitration/ArbitratorView.java b/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/arbitration/ArbitratorView.java index 90e1ceb0935..e1ac841c4d3 100644 --- a/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/arbitration/ArbitratorView.java +++ b/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/arbitration/ArbitratorView.java @@ -17,8 +17,6 @@ package bisq.desktop.main.support.dispute.agent.arbitration; -import bisq.common.config.Config; -import bisq.common.util.Utilities; import bisq.desktop.common.view.FxmlView; import bisq.desktop.main.overlays.windows.ContractWindow; import bisq.desktop.main.overlays.windows.DisputeSummaryWindow; @@ -30,6 +28,7 @@ import bisq.core.account.witness.AccountAgeWitnessService; import bisq.core.alert.PrivateNotificationManager; +import bisq.core.dao.DaoFacade; import bisq.core.support.SupportType; import bisq.core.support.dispute.Dispute; import bisq.core.support.dispute.DisputeSession; @@ -39,14 +38,16 @@ import bisq.core.util.FormattingUtils; import bisq.core.util.coin.CoinFormatter; +import bisq.common.config.Config; import bisq.common.crypto.KeyRing; +import bisq.common.util.Utilities; +import javax.inject.Inject; import javax.inject.Named; + import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; -import javax.inject.Inject; - @FxmlView public class ArbitratorView extends DisputeAgentView { @@ -65,6 +66,7 @@ public ArbitratorView(ArbitrationManager arbitrationManager, TradeDetailsWindow tradeDetailsWindow, AccountAgeWitnessService accountAgeWitnessService, @Named(Config.USE_DEV_PRIVILEGE_KEYS) boolean useDevPrivilegeKeys, + DaoFacade daoFacade, SignPaymentAccountsWindow signPaymentAccountsWindow, SignSpecificWitnessWindow signSpecificWitnessWindow, SignUnsignedPubKeysWindow signUnsignedPubKeysWindow) { @@ -77,6 +79,7 @@ public ArbitratorView(ArbitrationManager arbitrationManager, contractWindow, tradeDetailsWindow, accountAgeWitnessService, + daoFacade, useDevPrivilegeKeys); this.signPaymentAccountsWindow = signPaymentAccountsWindow; this.signSpecificWitnessWindow = signSpecificWitnessWindow; diff --git a/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/mediation/MediatorView.java b/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/mediation/MediatorView.java index 44601a2d249..61cdb63c6e2 100644 --- a/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/mediation/MediatorView.java +++ b/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/mediation/MediatorView.java @@ -25,6 +25,7 @@ import bisq.core.account.witness.AccountAgeWitnessService; import bisq.core.alert.PrivateNotificationManager; +import bisq.core.dao.DaoFacade; import bisq.core.support.SupportType; import bisq.core.support.dispute.Dispute; import bisq.core.support.dispute.DisputeSession; @@ -37,8 +38,8 @@ import bisq.common.config.Config; import bisq.common.crypto.KeyRing; -import javax.inject.Named; import javax.inject.Inject; +import javax.inject.Named; @FxmlView public class MediatorView extends DisputeAgentView { @@ -53,6 +54,7 @@ public MediatorView(MediationManager mediationManager, ContractWindow contractWindow, TradeDetailsWindow tradeDetailsWindow, AccountAgeWitnessService accountAgeWitnessService, + DaoFacade daoFacade, @Named(Config.USE_DEV_PRIVILEGE_KEYS) boolean useDevPrivilegeKeys) { super(mediationManager, keyRing, @@ -63,6 +65,7 @@ public MediatorView(MediationManager mediationManager, contractWindow, tradeDetailsWindow, accountAgeWitnessService, + daoFacade, useDevPrivilegeKeys); } diff --git a/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/refund/RefundAgentView.java b/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/refund/RefundAgentView.java index 5aae5f53f81..71f27f8954e 100644 --- a/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/refund/RefundAgentView.java +++ b/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/refund/RefundAgentView.java @@ -25,6 +25,7 @@ import bisq.core.account.witness.AccountAgeWitnessService; import bisq.core.alert.PrivateNotificationManager; +import bisq.core.dao.DaoFacade; import bisq.core.support.SupportType; import bisq.core.support.dispute.Dispute; import bisq.core.support.dispute.DisputeSession; @@ -37,9 +38,8 @@ import bisq.common.config.Config; import bisq.common.crypto.KeyRing; -import javax.inject.Named; - import javax.inject.Inject; +import javax.inject.Named; @FxmlView public class RefundAgentView extends DisputeAgentView { @@ -54,6 +54,7 @@ public RefundAgentView(RefundManager refundManager, ContractWindow contractWindow, TradeDetailsWindow tradeDetailsWindow, AccountAgeWitnessService accountAgeWitnessService, + DaoFacade daoFacade, @Named(Config.USE_DEV_PRIVILEGE_KEYS) boolean useDevPrivilegeKeys) { super(refundManager, keyRing, @@ -64,6 +65,7 @@ public RefundAgentView(RefundManager refundManager, contractWindow, tradeDetailsWindow, accountAgeWitnessService, + daoFacade, useDevPrivilegeKeys); } diff --git a/proto/src/main/proto/pb.proto b/proto/src/main/proto/pb.proto index 117dbb4cb53..68bfc8dd505 100644 --- a/proto/src/main/proto/pb.proto +++ b/proto/src/main/proto/pb.proto @@ -787,6 +787,7 @@ message Dispute { SupportType support_type = 24; string mediators_dispute_result = 25; string delayed_payout_tx_id = 26; + string donation_address_of_delayed_payout_tx = 27; } message Attachment {