mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-24 23:18:17 +01:00
Merge branch 'verify-donation-address-for-refund-agent' into dispute-agent-branch
# Conflicts: # core/src/main/java/bisq/core/support/dispute/DisputeManager.java # desktop/src/main/java/bisq/desktop/main/support/dispute/agent/DisputeAgentView.java
This commit is contained in:
commit
b2a9262b93
11 changed files with 277 additions and 61 deletions
|
@ -73,6 +73,7 @@ import bisq.core.dao.state.model.governance.Vote;
|
|||
|
||||
import bisq.asset.Asset;
|
||||
|
||||
import bisq.common.config.Config;
|
||||
import bisq.common.handlers.ErrorMessageHandler;
|
||||
import bisq.common.handlers.ExceptionHandler;
|
||||
import bisq.common.handlers.ResultHandler;
|
||||
|
@ -766,12 +767,16 @@ public class DaoFacade implements DaoSetupService {
|
|||
Set<String> allPastParamValues = getAllPastParamValues(Param.RECIPIENT_BTC_ADDRESS);
|
||||
|
||||
// If Dao is deactivated we need to add the default address as getAllPastParamValues will not return us any.
|
||||
allPastParamValues.add(Param.RECIPIENT_BTC_ADDRESS.getDefaultValue());
|
||||
if (allPastParamValues.isEmpty()) {
|
||||
allPastParamValues.add(Param.RECIPIENT_BTC_ADDRESS.getDefaultValue());
|
||||
}
|
||||
|
||||
// If Dao is deactivated we need to add the past addresses used as well.
|
||||
// This list need to be updated once a new address gets defined.
|
||||
allPastParamValues.add("3EtUWqsGThPtjwUczw27YCo6EWvQdaPUyp"); // burning man 2019
|
||||
allPastParamValues.add("3A8Zc1XioE2HRzYfbb5P8iemCS72M6vRJV"); // burningman2
|
||||
if (Config.baseCurrencyNetwork().isMainnet()) {
|
||||
// If Dao is deactivated we need to add the past addresses used as well.
|
||||
// This list need to be updated once a new address gets defined.
|
||||
allPastParamValues.add("3EtUWqsGThPtjwUczw27YCo6EWvQdaPUyp"); // burning man 2019
|
||||
allPastParamValues.add("3A8Zc1XioE2HRzYfbb5P8iemCS72M6vRJV"); // burningman2
|
||||
}
|
||||
|
||||
return allPastParamValues;
|
||||
}
|
||||
|
|
|
@ -107,6 +107,9 @@ public final class Dispute implements NetworkPayload {
|
|||
@Setter
|
||||
@Nullable
|
||||
private String donationAddressOfDelayedPayoutTx;
|
||||
@Setter
|
||||
@Nullable
|
||||
private String agentsUid;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -234,6 +237,7 @@ public final class Dispute implements NetworkPayload {
|
|||
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(agentsUid).ifPresent(result -> builder.setAgentsUid(agentsUid));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
@ -282,6 +286,11 @@ public final class Dispute implements NetworkPayload {
|
|||
dispute.setDonationAddressOfDelayedPayoutTx(donationAddressOfDelayedPayoutTx);
|
||||
}
|
||||
|
||||
String agentsUid = proto.getAgentsUid();
|
||||
if (!agentsUid.isEmpty()) {
|
||||
dispute.setAgentsUid(agentsUid);
|
||||
}
|
||||
|
||||
return dispute;
|
||||
}
|
||||
|
||||
|
|
|
@ -89,10 +89,11 @@ public abstract class DisputeManager<T extends DisputeList<? extends DisputeList
|
|||
protected final PubKeyRing pubKeyRing;
|
||||
protected final DisputeListService<T> disputeListService;
|
||||
private final PriceFeedService priceFeedService;
|
||||
private final DaoFacade daoFacade;
|
||||
protected final DaoFacade daoFacade;
|
||||
|
||||
@Getter
|
||||
protected final ObservableList<Dispute> disputesWithInvalidDonationAddress = FXCollections.observableArrayList();
|
||||
protected final ObservableList<DelayedPayoutTxValidation.ValidationException> validationExceptions =
|
||||
FXCollections.observableArrayList();
|
||||
@Getter
|
||||
private final KeyPair signatureKeyPair;
|
||||
|
||||
|
@ -225,7 +226,7 @@ public abstract class DisputeManager<T extends DisputeList<? extends DisputeList
|
|||
return disputeListService.getNrOfDisputes(isBuyer, contract);
|
||||
}
|
||||
|
||||
private T getDisputeList() {
|
||||
protected T getDisputeList() {
|
||||
return disputeListService.getDisputeList();
|
||||
}
|
||||
|
||||
|
@ -257,6 +258,20 @@ public abstract class DisputeManager<T extends DisputeList<? extends DisputeList
|
|||
|
||||
tryApplyMessages();
|
||||
cleanupDisputes();
|
||||
|
||||
getDisputeList().getList().forEach(dispute -> {
|
||||
if (dispute.getAgentsUid() == null) {
|
||||
dispute.setAgentsUid(UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
try {
|
||||
DelayedPayoutTxValidation.validateDonationAddress(dispute, dispute.getDonationAddressOfDelayedPayoutTx(), daoFacade);
|
||||
DelayedPayoutTxValidation.testIfDisputeTriesReplay(dispute, getDisputeList().getList());
|
||||
} catch (DelayedPayoutTxValidation.AddressException | DelayedPayoutTxValidation.DisputeReplayException e) {
|
||||
log.error(e.toString());
|
||||
validationExceptions.add(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean isTrader(Dispute dispute) {
|
||||
|
@ -287,6 +302,8 @@ public abstract class DisputeManager<T extends DisputeList<? extends DisputeList
|
|||
|
||||
String errorMessage = null;
|
||||
Dispute dispute = openNewDisputeMessage.getDispute();
|
||||
// Dispute agent sets uid to be sure to identify disputes uniquely to protect against replaying old disputes
|
||||
dispute.setAgentsUid(UUID.randomUUID().toString());
|
||||
dispute.setStorage(disputeListService.getStorage());
|
||||
// Disputes from clients < 1.2.0 always have support type ARBITRATION in dispute as the field didn't exist before
|
||||
dispute.setSupportType(openNewDisputeMessage.getSupportType());
|
||||
|
@ -296,8 +313,10 @@ public abstract class DisputeManager<T extends DisputeList<? extends DisputeList
|
|||
|
||||
try {
|
||||
DelayedPayoutTxValidation.validateDonationAddress(dispute.getDonationAddressOfDelayedPayoutTx(), daoFacade);
|
||||
} catch (DelayedPayoutTxValidation.AddressException e) {
|
||||
disputesWithInvalidDonationAddress.add(dispute);
|
||||
DelayedPayoutTxValidation.testIfDisputeTriesReplay(dispute, disputeList.getList());
|
||||
} catch (DelayedPayoutTxValidation.AddressException | DelayedPayoutTxValidation.DisputeReplayException e) {
|
||||
log.error(e.toString());
|
||||
validationExceptions.add(e);
|
||||
}
|
||||
|
||||
PubKeyRing peersPubKeyRing = dispute.isDisputeOpenerIsBuyer() ? contract.getSellerPubKeyRing() : contract.getBuyerPubKeyRing();
|
||||
|
@ -585,6 +604,9 @@ public abstract class DisputeManager<T extends DisputeList<? extends DisputeList
|
|||
|
||||
addPriceInfoMessage(dispute, 0);
|
||||
|
||||
// Dispute agent sets uid to be sure to identify disputes uniquely to protect against replaying old disputes
|
||||
dispute.setAgentsUid(UUID.randomUUID().toString());
|
||||
|
||||
disputeList.add(dispute);
|
||||
|
||||
// We mirrored dispute already!
|
||||
|
|
|
@ -89,6 +89,7 @@ public final class MediationManager extends DisputeManager<MediationDisputeList>
|
|||
openOfferManager, daoFacade, keyRing, mediationDisputeListService, priceFeedService);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Implement template methods
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -83,6 +83,7 @@ public final class RefundManager extends DisputeManager<RefundDisputeList> {
|
|||
openOfferManager, daoFacade, keyRing, refundDisputeListService, priceFeedService);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Implement template methods
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -30,9 +30,14 @@ import org.bitcoinj.core.TransactionInput;
|
|||
import org.bitcoinj.core.TransactionOutPoint;
|
||||
import org.bitcoinj.core.TransactionOutput;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
@ -45,6 +50,11 @@ public class DelayedPayoutTxValidation {
|
|||
|
||||
public static void validateDonationAddress(String addressAsString, DaoFacade daoFacade)
|
||||
throws AddressException {
|
||||
validateDonationAddress(null, addressAsString, daoFacade);
|
||||
}
|
||||
|
||||
public static void validateDonationAddress(@Nullable Dispute dispute, String addressAsString, DaoFacade daoFacade)
|
||||
throws AddressException {
|
||||
|
||||
if (addressAsString == null) {
|
||||
log.warn("address is null at validateDonationAddress. This is expected in case of an not updated trader.");
|
||||
|
@ -57,7 +67,55 @@ public class DelayedPayoutTxValidation {
|
|||
"\nAddress used in the dispute: " + addressAsString +
|
||||
"\nAll DAO param donation addresses:" + allPastParamValues;
|
||||
log.error(errorMsg);
|
||||
throw new AddressException(errorMsg);
|
||||
throw new AddressException(dispute, errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void testIfDisputeTriesReplay(Dispute disputeToTest, List<Dispute> disputeList)
|
||||
throws DisputeReplayException {
|
||||
try {
|
||||
String disputeToTestDelayedPayoutTxId = disputeToTest.getDelayedPayoutTxId();
|
||||
checkNotNull(disputeToTestDelayedPayoutTxId,
|
||||
"delayedPayoutTxId must not be null. Trade ID: " + disputeToTest.getTradeId());
|
||||
String disputeToTestAgentsUid = checkNotNull(disputeToTest.getAgentsUid(),
|
||||
"agentsUid must not be null. Trade ID: " + disputeToTest.getTradeId());
|
||||
// This method can be called with the existing list and a new dispute (at opening a new dispute) or with the
|
||||
// dispute already added (at close dispute). So we will consider that in the for loop.
|
||||
// We have 2 disputes per trade (one per trader).
|
||||
|
||||
Map<String, Set<String>> disputesPerTradeId = new HashMap<>();
|
||||
Map<String, Set<String>> disputesPerDelayedPayoutTxId = new HashMap<>();
|
||||
disputeList.forEach(dispute -> {
|
||||
String tradeId = dispute.getTradeId();
|
||||
String agentsUid = dispute.getAgentsUid();
|
||||
|
||||
// We use an uid we have created not data delivered by the trader to protect against replay attacks
|
||||
// If our dispute got already added to the list we ignore it. We will check once we build our maps
|
||||
|
||||
disputesPerTradeId.putIfAbsent(tradeId, new HashSet<>());
|
||||
Set<String> set = disputesPerTradeId.get(tradeId);
|
||||
if (!disputeToTestAgentsUid.equals(agentsUid)) {
|
||||
set.add(agentsUid);
|
||||
}
|
||||
|
||||
String delayedPayoutTxId = dispute.getDelayedPayoutTxId();
|
||||
disputesPerDelayedPayoutTxId.putIfAbsent(delayedPayoutTxId, new HashSet<>());
|
||||
set = disputesPerDelayedPayoutTxId.get(delayedPayoutTxId);
|
||||
if (!disputeToTestAgentsUid.equals(agentsUid)) {
|
||||
set.add(agentsUid);
|
||||
}
|
||||
});
|
||||
|
||||
String disputeToTestTradeId = disputeToTest.getTradeId();
|
||||
checkArgument(disputesPerTradeId.get(disputeToTestTradeId).size() <= 1,
|
||||
"We found more then 2 disputes with the same trade ID. " +
|
||||
"Trade ID: " + disputeToTest.getTradeId());
|
||||
checkArgument(disputesPerDelayedPayoutTxId.get(disputeToTestDelayedPayoutTxId).size() <= 1,
|
||||
"We found more then 2 disputes with the same delayedPayoutTxId. " +
|
||||
"Trade ID: " + disputeToTest.getTradeId());
|
||||
|
||||
} catch (IllegalArgumentException | NullPointerException e) {
|
||||
throw new DisputeReplayException(disputeToTest, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,7 +235,7 @@ public class DelayedPayoutTxValidation {
|
|||
errorMsg = "Donation address cannot be resolved (not of type P2PKHScript or P2SH). Output: " + output;
|
||||
log.error(errorMsg);
|
||||
log.error(delayedPayoutTx.toString());
|
||||
throw new AddressException(errorMsg);
|
||||
throw new AddressException(dispute, errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -220,14 +278,23 @@ public class DelayedPayoutTxValidation {
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static class ValidationException extends Exception {
|
||||
@Nullable
|
||||
@Getter
|
||||
private final Dispute dispute;
|
||||
|
||||
ValidationException(String msg) {
|
||||
this(null, msg);
|
||||
}
|
||||
|
||||
ValidationException(@Nullable Dispute dispute, String msg) {
|
||||
super(msg);
|
||||
this.dispute = dispute;
|
||||
}
|
||||
}
|
||||
|
||||
public static class AddressException extends ValidationException {
|
||||
AddressException(String msg) {
|
||||
super(msg);
|
||||
AddressException(@Nullable Dispute dispute, String msg) {
|
||||
super(dispute, msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -260,4 +327,10 @@ public class DelayedPayoutTxValidation {
|
|||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static class DisputeReplayException extends ValidationException {
|
||||
DisputeReplayException(Dispute dispute, String msg) {
|
||||
super(dispute, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -758,6 +758,7 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
var disputeManager = checkNotNull(getDisputeManager(dispute));
|
||||
try {
|
||||
DelayedPayoutTxValidation.validateDonationAddress(dispute.getDonationAddressOfDelayedPayoutTx(), daoFacade);
|
||||
DelayedPayoutTxValidation.testIfDisputeTriesReplay(dispute, disputeManager.getDisputesAsObservableList());
|
||||
doClose(closeTicketButton);
|
||||
} catch (DelayedPayoutTxValidation.AddressException exception) {
|
||||
String addressAsString = dispute.getDonationAddressOfDelayedPayoutTx();
|
||||
|
@ -787,6 +788,21 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
Res.get("support.warning.disputesWithInvalidDonationAddress.refundAgent")))
|
||||
.show();
|
||||
}
|
||||
} catch (DelayedPayoutTxValidation.DisputeReplayException exception) {
|
||||
if (disputeManager instanceof MediationManager) {
|
||||
new Popup().width(900)
|
||||
.warning(exception.getMessage())
|
||||
.onAction(() -> {
|
||||
doClose(closeTicketButton);
|
||||
})
|
||||
.actionButtonText(Res.get("shared.yes"))
|
||||
.closeButtonText(Res.get("shared.no"))
|
||||
.show();
|
||||
} else {
|
||||
new Popup().width(900)
|
||||
.warning(exception.getMessage())
|
||||
.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -92,6 +92,7 @@ import javafx.collections.transformation.FilteredList;
|
|||
import javafx.collections.transformation.SortedList;
|
||||
|
||||
import javafx.util.Callback;
|
||||
import javafx.util.Duration;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
|
@ -105,6 +106,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
|
@ -113,6 +115,29 @@ import javax.annotation.Nullable;
|
|||
import static bisq.desktop.util.FormBuilder.getIconForLabel;
|
||||
|
||||
public abstract class DisputeView extends ActivatableView<VBox, Void> {
|
||||
public enum FilterResult {
|
||||
NO_MATCH("No Match"),
|
||||
NO_FILTER("No filter text"),
|
||||
OPEN_DISPUTES("Open disputes"),
|
||||
TRADE_ID("Trade ID"),
|
||||
OPENING_DATE("Opening date"),
|
||||
BUYER_NODE_ADDRESS("Buyer node address"),
|
||||
SELLER_NODE_ADDRESS("Seller node address"),
|
||||
BUYER_ACCOUNT_DETAILS("Buyer account details"),
|
||||
SELLER_ACCOUNT_DETAILS("Seller account details"),
|
||||
DEPOSIT_TX("Deposit tx ID"),
|
||||
PAYOUT_TX("Payout tx ID"),
|
||||
DEL_PAYOUT_TX("Delayed payout tx ID"),
|
||||
JSON("Contract as json");
|
||||
|
||||
@Getter
|
||||
private final String displayString;
|
||||
|
||||
FilterResult(String displayString) {
|
||||
|
||||
this.displayString = displayString;
|
||||
}
|
||||
}
|
||||
|
||||
protected final DisputeManager<? extends DisputeList<? extends DisputeList>> disputeManager;
|
||||
protected final KeyRing keyRing;
|
||||
|
@ -186,6 +211,10 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> {
|
|||
HBox.setHgrow(label, Priority.NEVER);
|
||||
|
||||
filterTextField = new InputTextField();
|
||||
Tooltip tooltip = new Tooltip();
|
||||
tooltip.setShowDelay(Duration.millis(100));
|
||||
tooltip.setShowDuration(Duration.seconds(10));
|
||||
filterTextField.setTooltip(tooltip);
|
||||
filterTextFieldListener = (observable, oldValue, newValue) -> applyFilteredListPredicate(filterTextField.getText());
|
||||
HBox.setHgrow(filterTextField, Priority.NEVER);
|
||||
|
||||
|
@ -401,10 +430,77 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> {
|
|||
protected abstract DisputeSession getConcreteDisputeChatSession(Dispute dispute);
|
||||
|
||||
protected void applyFilteredListPredicate(String filterString) {
|
||||
// If in trader view we must not display arbitrators own disputes as trader (must not happen anyway)
|
||||
filteredList.setPredicate(dispute -> !dispute.getAgentPubKeyRing().equals(keyRing.getPubKeyRing()));
|
||||
AtomicReference<FilterResult> filterResult = new AtomicReference<>(FilterResult.NO_FILTER);
|
||||
filteredList.setPredicate(dispute -> {
|
||||
FilterResult filterResult1 = getFilterResult(dispute, filterString);
|
||||
filterResult.set(filterResult1);
|
||||
boolean b = filterResult.get() != FilterResult.NO_MATCH;
|
||||
log.error("filterResult1 {} {} {}, {}", filterResult1, dispute.getTraderId(), b, filterResult);
|
||||
return b;
|
||||
});
|
||||
|
||||
if (filterResult.get() == FilterResult.NO_MATCH) {
|
||||
filterTextField.getTooltip().setText("No matches found");
|
||||
} else if (filterResult.get() == FilterResult.NO_FILTER) {
|
||||
filterTextField.getTooltip().setText("No filter applied");
|
||||
} else if (filterResult.get() == FilterResult.OPEN_DISPUTES) {
|
||||
filterTextField.getTooltip().setText("Show all open disputes");
|
||||
} else {
|
||||
filterTextField.getTooltip().setText("Data matching filter string: " + filterResult.get().getDisplayString());
|
||||
}
|
||||
}
|
||||
|
||||
protected FilterResult getFilterResult(Dispute dispute, String filterString) {
|
||||
if (filterString.isEmpty()) {
|
||||
return FilterResult.NO_FILTER;
|
||||
}
|
||||
if (!dispute.isClosed() && filterString.toLowerCase().equals("open")) {
|
||||
return FilterResult.OPEN_DISPUTES;
|
||||
}
|
||||
|
||||
if (dispute.getTradeId().contains(filterString)) {
|
||||
return FilterResult.TRADE_ID;
|
||||
}
|
||||
|
||||
if (DisplayUtils.formatDate(dispute.getOpeningDate()).contains(filterString)) {
|
||||
return FilterResult.OPENING_DATE;
|
||||
}
|
||||
|
||||
if (dispute.getContract().getBuyerNodeAddress().getFullAddress().contains(filterString)) {
|
||||
return FilterResult.BUYER_NODE_ADDRESS;
|
||||
}
|
||||
|
||||
if (dispute.getContract().getSellerNodeAddress().getFullAddress().contains(filterString)) {
|
||||
return FilterResult.SELLER_NODE_ADDRESS;
|
||||
}
|
||||
|
||||
if (dispute.getContract().getBuyerPaymentAccountPayload().getPaymentDetails().contains(filterString)) {
|
||||
return FilterResult.BUYER_ACCOUNT_DETAILS;
|
||||
}
|
||||
|
||||
if (dispute.getContract().getSellerPaymentAccountPayload().getPaymentDetails().contains(filterString)) {
|
||||
return FilterResult.SELLER_ACCOUNT_DETAILS;
|
||||
}
|
||||
|
||||
if (dispute.getDepositTxId() != null && dispute.getDepositTxId().contains(filterString)) {
|
||||
return FilterResult.DEPOSIT_TX;
|
||||
}
|
||||
if (dispute.getPayoutTxId() != null && dispute.getPayoutTxId().contains(filterString)) {
|
||||
return FilterResult.PAYOUT_TX;
|
||||
}
|
||||
|
||||
if (dispute.getDelayedPayoutTxId() != null && dispute.getDelayedPayoutTxId().contains(filterString)) {
|
||||
return FilterResult.DEL_PAYOUT_TX;
|
||||
}
|
||||
|
||||
if (dispute.getContractAsJson().contains(filterString)) {
|
||||
return FilterResult.JSON;
|
||||
}
|
||||
|
||||
return FilterResult.NO_MATCH;
|
||||
}
|
||||
|
||||
|
||||
protected void reOpenDisputeFromButton() {
|
||||
reOpenDispute();
|
||||
}
|
||||
|
@ -428,16 +524,6 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> {
|
|||
}
|
||||
}
|
||||
|
||||
protected boolean anyMatchOfFilterString(Dispute dispute, String filterString) {
|
||||
boolean matchesTradeId = dispute.getTradeId().contains(filterString);
|
||||
boolean matchesDate = DisplayUtils.formatDate(dispute.getOpeningDate()).contains(filterString);
|
||||
boolean isBuyerOnion = dispute.getContract().getBuyerNodeAddress().getFullAddress().contains(filterString);
|
||||
boolean isSellerOnion = dispute.getContract().getSellerNodeAddress().getFullAddress().contains(filterString);
|
||||
boolean matchesBuyersPaymentAccountData = dispute.getContract().getBuyerPaymentAccountPayload().getPaymentDetails().contains(filterString);
|
||||
boolean matchesSellersPaymentAccountData = dispute.getContract().getSellerPaymentAccountPayload().getPaymentDetails().contains(filterString);
|
||||
return matchesTradeId || matchesDate || isBuyerOnion || isSellerOnion ||
|
||||
matchesBuyersPaymentAccountData || matchesSellersPaymentAccountData;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// UI actions
|
||||
|
|
|
@ -36,6 +36,7 @@ import bisq.core.support.dispute.DisputeSession;
|
|||
import bisq.core.support.dispute.agent.MultipleHolderNameDetection;
|
||||
import bisq.core.support.dispute.mediation.mediator.MediatorManager;
|
||||
import bisq.core.support.dispute.refund.refundagent.RefundAgentManager;
|
||||
import bisq.core.trade.DelayedPayoutTxValidation;
|
||||
import bisq.core.trade.TradeManager;
|
||||
import bisq.core.user.DontShowAgainLookup;
|
||||
import bisq.core.util.coin.CoinFormatter;
|
||||
|
@ -61,13 +62,14 @@ import javafx.collections.ListChangeListener;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import static bisq.core.trade.DelayedPayoutTxValidation.ValidationException;
|
||||
import static bisq.desktop.util.FormBuilder.getIconForLabel;
|
||||
|
||||
public abstract class DisputeAgentView extends DisputeView implements MultipleHolderNameDetection.Listener {
|
||||
|
||||
private final MultipleHolderNameDetection multipleHolderNameDetection;
|
||||
private final DaoFacade daoFacade;
|
||||
private ListChangeListener<Dispute> disputesWithInvalidDonationAddressListener;
|
||||
private ListChangeListener<ValidationException> validationExceptionListener;
|
||||
|
||||
public DisputeAgentView(DisputeManager<? extends DisputeList<? extends DisputeList>> disputeManager,
|
||||
KeyRing keyRing,
|
||||
|
@ -121,24 +123,30 @@ public abstract class DisputeAgentView extends DisputeView implements MultipleHo
|
|||
|
||||
multipleHolderNameDetection.detectMultipleHolderNames();
|
||||
|
||||
disputesWithInvalidDonationAddressListener = c -> {
|
||||
validationExceptionListener = c -> {
|
||||
c.next();
|
||||
if (c.wasAdded()) {
|
||||
showWarningForInvalidDonationAddress(c.getAddedSubList());
|
||||
showWarningForValidationExceptions(c.getAddedSubList());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected void showWarningForInvalidDonationAddress(List<? extends Dispute> disputes) {
|
||||
disputes.stream()
|
||||
.filter(dispute -> !dispute.isClosed())
|
||||
.forEach(dispute -> {
|
||||
new Popup().warning(Res.get("support.warning.disputesWithInvalidDonationAddress",
|
||||
dispute.getDonationAddressOfDelayedPayoutTx(),
|
||||
daoFacade.getAllDonationAddresses(),
|
||||
dispute.getTradeId(),
|
||||
""))
|
||||
.show();
|
||||
protected void showWarningForValidationExceptions(List<? extends ValidationException> exceptions) {
|
||||
exceptions.stream()
|
||||
.filter(ex -> ex.getDispute() != null)
|
||||
.filter(ex -> !ex.getDispute().isClosed())
|
||||
.forEach(ex -> {
|
||||
Dispute dispute = ex.getDispute();
|
||||
if (ex instanceof DelayedPayoutTxValidation.AddressException) {
|
||||
new Popup().width(900).warning(Res.get("support.warning.disputesWithInvalidDonationAddress",
|
||||
dispute.getDonationAddressOfDelayedPayoutTx(),
|
||||
daoFacade.getAllDonationAddresses(),
|
||||
dispute.getTradeId(),
|
||||
""))
|
||||
.show();
|
||||
} else {
|
||||
new Popup().width(900).warning(ex.getMessage()).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -151,8 +159,8 @@ public abstract class DisputeAgentView extends DisputeView implements MultipleHo
|
|||
suspiciousDisputeDetected();
|
||||
}
|
||||
|
||||
disputeManager.getDisputesWithInvalidDonationAddress().addListener(disputesWithInvalidDonationAddressListener);
|
||||
showWarningForInvalidDonationAddress(disputeManager.getDisputesWithInvalidDonationAddress());
|
||||
disputeManager.getValidationExceptions().addListener(validationExceptionListener);
|
||||
showWarningForValidationExceptions(disputeManager.getValidationExceptions());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -161,7 +169,7 @@ public abstract class DisputeAgentView extends DisputeView implements MultipleHo
|
|||
|
||||
multipleHolderNameDetection.removeListener(this);
|
||||
|
||||
disputeManager.getDisputesWithInvalidDonationAddress().removeListener(disputesWithInvalidDonationAddressListener);
|
||||
disputeManager.getValidationExceptions().removeListener(validationExceptionListener);
|
||||
}
|
||||
|
||||
|
||||
|
@ -180,17 +188,13 @@ public abstract class DisputeAgentView extends DisputeView implements MultipleHo
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
protected void applyFilteredListPredicate(String filterString) {
|
||||
filteredList.setPredicate(dispute -> {
|
||||
// If in arbitrator view we must only display disputes where we are selected as arbitrator (must not receive others anyway)
|
||||
if (!dispute.getAgentPubKeyRing().equals(keyRing.getPubKeyRing())) {
|
||||
return false;
|
||||
}
|
||||
boolean isOpen = !dispute.isClosed() && filterString.toLowerCase().equals("open");
|
||||
return filterString.isEmpty() ||
|
||||
isOpen ||
|
||||
anyMatchOfFilterString(dispute, filterString);
|
||||
});
|
||||
protected DisputeView.FilterResult getFilterResult(Dispute dispute, String filterString) {
|
||||
// If in arbitrator view we must only display disputes where we are selected as arbitrator (must not receive others anyway)
|
||||
if (!dispute.getAgentPubKeyRing().equals(keyRing.getPubKeyRing())) {
|
||||
return FilterResult.NO_MATCH;
|
||||
}
|
||||
|
||||
return super.getFilterResult(dispute, filterString);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -60,14 +60,12 @@ public abstract class DisputeClientView extends DisputeView {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void applyFilteredListPredicate(String filterString) {
|
||||
filteredList.setPredicate(dispute -> {
|
||||
// As we are in the client view we hide disputes where we are the agent
|
||||
if (dispute.getAgentPubKeyRing().equals(keyRing.getPubKeyRing())) {
|
||||
return false;
|
||||
}
|
||||
protected DisputeView.FilterResult getFilterResult(Dispute dispute, String filterString) {
|
||||
// As we are in the client view we hide disputes where we are the agent
|
||||
if (dispute.getAgentPubKeyRing().equals(keyRing.getPubKeyRing())) {
|
||||
return FilterResult.NO_MATCH;
|
||||
}
|
||||
|
||||
return filterString.isEmpty() || anyMatchOfFilterString(dispute, filterString);
|
||||
});
|
||||
return super.getFilterResult(dispute, filterString);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -793,6 +793,7 @@ message Dispute {
|
|||
string mediators_dispute_result = 25;
|
||||
string delayed_payout_tx_id = 26;
|
||||
string donation_address_of_delayed_payout_tx = 27;
|
||||
string agents_uid = 28;
|
||||
}
|
||||
|
||||
message Attachment {
|
||||
|
|
Loading…
Add table
Reference in a new issue