mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-08-13 00:45:29 -04:00
Add API functions to open and resolve disputes (#244)
Co-authored-by: woodser <woodser@protonmail.com>
This commit is contained in:
parent
07c48a04f5
commit
e7b4627102
22 changed files with 752 additions and 306 deletions
|
@ -228,8 +228,8 @@ public class NotificationCenter {
|
|||
|
||||
private void onDisputeStateChanged(Trade trade, Trade.DisputeState disputeState) {
|
||||
String message = null;
|
||||
if (refundManager.findOwnDispute(trade.getId()).isPresent()) {
|
||||
String disputeOrTicket = refundManager.findOwnDispute(trade.getId()).get().isSupportTicket() ?
|
||||
if (refundManager.findDispute(trade.getId()).isPresent()) {
|
||||
String disputeOrTicket = refundManager.findDispute(trade.getId()).get().isSupportTicket() ?
|
||||
Res.get("shared.supportTicket") :
|
||||
Res.get("shared.dispute");
|
||||
switch (disputeState) {
|
||||
|
@ -253,8 +253,8 @@ public class NotificationCenter {
|
|||
if (message != null) {
|
||||
goToSupport(trade, message, false);
|
||||
}
|
||||
} else if (mediationManager.findOwnDispute(trade.getId()).isPresent()) {
|
||||
String disputeOrTicket = mediationManager.findOwnDispute(trade.getId()).get().isSupportTicket() ?
|
||||
} else if (mediationManager.findDispute(trade.getId()).isPresent()) {
|
||||
String disputeOrTicket = mediationManager.findDispute(trade.getId()).get().isSupportTicket() ?
|
||||
Res.get("shared.supportTicket") :
|
||||
Res.get("shared.mediationCase");
|
||||
switch (disputeState) {
|
||||
|
|
|
@ -23,7 +23,8 @@ import bisq.desktop.components.HavenoTextArea;
|
|||
import bisq.desktop.components.InputTextField;
|
||||
import bisq.desktop.main.overlays.Overlay;
|
||||
import bisq.desktop.main.overlays.popups.Popup;
|
||||
import bisq.desktop.main.support.dispute.DisputeSummaryVerification;
|
||||
|
||||
import bisq.core.api.CoreDisputesService;
|
||||
import bisq.desktop.util.DisplayUtils;
|
||||
import bisq.desktop.util.Layout;
|
||||
|
||||
|
@ -49,7 +50,6 @@ import bisq.common.handlers.ResultHandler;
|
|||
import bisq.common.util.Tuple2;
|
||||
import bisq.common.util.Tuple3;
|
||||
|
||||
import org.bitcoinj.core.AddressFormatException;
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
@ -86,11 +86,6 @@ import static bisq.desktop.util.FormBuilder.addTitledGroupBg;
|
|||
import static bisq.desktop.util.FormBuilder.addTopLabelWithVBox;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
|
||||
|
||||
import monero.wallet.MoneroWallet;
|
||||
import monero.wallet.model.MoneroTxWallet;
|
||||
|
||||
@Slf4j
|
||||
public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
||||
private final CoinFormatter formatter;
|
||||
|
@ -98,8 +93,8 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
private final MediationManager mediationManager;
|
||||
private final XmrWalletService walletService;
|
||||
private final TradeWalletService tradeWalletService; // TODO (woodser): remove for xmr or adapt to get/create multisig wallets for tx creation utils
|
||||
private final CoreDisputesService disputesService;
|
||||
private Dispute dispute;
|
||||
private Optional<Runnable> finalizeDisputeHandlerOptional = Optional.empty();
|
||||
private ToggleGroup tradeAmountToggleGroup, reasonToggleGroup;
|
||||
private DisputeResult disputeResult;
|
||||
private RadioButton buyerGetsTradeAmountRadioButton, sellerGetsTradeAmountRadioButton,
|
||||
|
@ -132,13 +127,15 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
ArbitrationManager arbitrationManager,
|
||||
MediationManager mediationManager,
|
||||
XmrWalletService walletService,
|
||||
TradeWalletService tradeWalletService) {
|
||||
TradeWalletService tradeWalletService,
|
||||
CoreDisputesService disputesService) {
|
||||
|
||||
this.formatter = formatter;
|
||||
this.arbitrationManager = arbitrationManager;
|
||||
this.mediationManager = mediationManager;
|
||||
this.walletService = walletService;
|
||||
this.tradeWalletService = tradeWalletService;
|
||||
this.disputesService = disputesService;
|
||||
|
||||
type = Type.Confirmation;
|
||||
}
|
||||
|
@ -159,12 +156,6 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
}
|
||||
}
|
||||
|
||||
public DisputeSummaryWindow onFinalizeDispute(Runnable finalizeDisputeHandler) {
|
||||
this.finalizeDisputeHandlerOptional = Optional.of(finalizeDisputeHandler);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Protected
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -598,39 +589,7 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
Button cancelButton = tuple.second;
|
||||
|
||||
closeTicketButton.setOnAction(e -> {
|
||||
|
||||
// TODO (woodser): create disputed payout tx after showing payout tx confirmation, within doCloseIfValid() (see upstream/master)
|
||||
if (!dispute.isMediationDispute()) {
|
||||
try {
|
||||
System.out.println(disputeResult);
|
||||
MoneroWallet multisigWallet = walletService.getMultisigWallet(dispute.getTradeId());
|
||||
//dispute.getContract().getArbitratorPubKeyRing(); // TODO: support arbitrator pub key ring in contract?
|
||||
//disputeResult.setArbitratorPubKey(arbitratorAddressEntry.getPubKey());
|
||||
|
||||
// TODO (woodser): don't send signed tx if opener is not co-signer?
|
||||
// // determine if opener is co-signer
|
||||
// boolean openerIsWinner = (contract.getBuyerPubKeyRing().equals(dispute.getTraderPubKeyRing()) && disputeResult.getWinner() == Winner.BUYER) || (contract.getSellerPubKeyRing().equals(dispute.getTraderPubKeyRing()) && disputeResult.getWinner() == Winner.SELLER);
|
||||
// boolean openerIsCosigner = openerIsWinner || disputeResult.isLoserPublisher();
|
||||
// if (!openerIsCosigner) throw new RuntimeException("Need to query non-opener for updated multisig hex before creating tx");
|
||||
|
||||
// arbitrator creates and signs dispute payout tx if dispute is in context of opener, otherwise opener's peer must request payout tx by providing updated multisig hex
|
||||
boolean isOpener = dispute.isOpener();
|
||||
System.out.println("Is dispute opener: " + isOpener);
|
||||
if (isOpener) {
|
||||
MoneroTxWallet arbitratorPayoutTx = ArbitrationManager.arbitratorCreatesDisputedPayoutTx(contract, dispute, disputeResult, multisigWallet);
|
||||
System.out.println("Created arbitrator-signed payout tx: " + arbitratorPayoutTx);
|
||||
if (arbitratorPayoutTx != null) disputeResult.setArbitratorSignedPayoutTxHex(arbitratorPayoutTx.getTxSet().getMultisigTxHex());
|
||||
}
|
||||
|
||||
// send arbitrator's updated multisig hex with dispute result
|
||||
disputeResult.setArbitratorUpdatedMultisigHex(multisigWallet.getMultisigHex());
|
||||
} catch (AddressFormatException e2) {
|
||||
log.error("Error at close dispute", e2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// // TODO (woodser): handle with showPayoutTxConfirmation() / doCloseIfValid() in order to have confirmation window (see upstream/master)
|
||||
disputesService.resolveDisputePayout(dispute, disputeResult, contract);
|
||||
doClose(closeTicketButton);
|
||||
|
||||
// if (dispute.getDepositTxSerialized() == null) {
|
||||
|
@ -801,49 +760,12 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
return;
|
||||
}
|
||||
|
||||
summaryNotesTextArea.textProperty().unbindBidirectional(disputeResult.summaryNotesProperty());
|
||||
|
||||
boolean isRefundAgent = disputeManager instanceof RefundManager;
|
||||
disputeResult.setLoserPublisher(isLoserPublisherCheckBox.isSelected());
|
||||
disputeResult.setCloseDate(new Date());
|
||||
dispute.setDisputeResult(disputeResult);
|
||||
dispute.setIsClosed();
|
||||
DisputeResult.Reason reason = disputeResult.getReason();
|
||||
|
||||
summaryNotesTextArea.textProperty().unbindBidirectional(disputeResult.summaryNotesProperty());
|
||||
|
||||
String role = isRefundAgent ? Res.get("shared.refundAgent") : Res.get("shared.mediator");
|
||||
String agentNodeAddress = checkNotNull(disputeManager.getAgentNodeAddress(dispute)).getFullAddress();
|
||||
Contract contract = dispute.getContract();
|
||||
String currencyCode = contract.getOfferPayload().getCurrencyCode();
|
||||
String amount = formatter.formatCoinWithCode(contract.getTradeAmount());
|
||||
|
||||
String textToSign = Res.get("disputeSummaryWindow.close.msg",
|
||||
DisplayUtils.formatDateTime(disputeResult.getCloseDate()),
|
||||
role,
|
||||
agentNodeAddress,
|
||||
dispute.getShortTradeId(),
|
||||
currencyCode,
|
||||
amount,
|
||||
formatter.formatCoinWithCode(disputeResult.getBuyerPayoutAmount()),
|
||||
formatter.formatCoinWithCode(disputeResult.getSellerPayoutAmount()),
|
||||
Res.get("disputeSummaryWindow.reason." + reason.name()),
|
||||
disputeResult.summaryNotesProperty().get()
|
||||
);
|
||||
|
||||
if (reason == DisputeResult.Reason.OPTION_TRADE &&
|
||||
dispute.getChatMessages().size() > 1 &&
|
||||
dispute.getChatMessages().get(1).isSystemMessage()) {
|
||||
textToSign += "\n" + dispute.getChatMessages().get(1).getMessage() + "\n";
|
||||
}
|
||||
|
||||
String summaryText = DisputeSummaryVerification.signAndApply(disputeManager, disputeResult, textToSign);
|
||||
|
||||
if (isRefundAgent) {
|
||||
summaryText += Res.get("disputeSummaryWindow.close.nextStepsForRefundAgentArbitration");
|
||||
} else {
|
||||
summaryText += Res.get("disputeSummaryWindow.close.nextStepsForMediation");
|
||||
}
|
||||
|
||||
disputeManager.sendDisputeResultMessage(disputeResult, dispute, summaryText);
|
||||
disputesService.closeDispute(disputeManager, dispute, disputeResult, isRefundAgent);
|
||||
|
||||
if (peersDisputeOptional.isPresent() && !peersDisputeOptional.get().isClosed() && !DevEnv.isDevMode()) {
|
||||
UserThread.runAfter(() -> new Popup()
|
||||
|
@ -852,12 +774,8 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
200, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
finalizeDisputeHandlerOptional.ifPresent(Runnable::run);
|
||||
|
||||
disputeManager.requestPersistence();
|
||||
|
||||
closeTicketButton.disableProperty().unbind();
|
||||
|
||||
hide();
|
||||
}
|
||||
|
||||
|
@ -878,33 +796,24 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
}
|
||||
|
||||
private void applyPayoutAmountsToDisputeResult(Toggle selectedTradeAmountToggle) {
|
||||
Contract contract = dispute.getContract();
|
||||
Offer offer = new Offer(contract.getOfferPayload());
|
||||
Coin buyerSecurityDeposit = offer.getBuyerSecurityDeposit();
|
||||
Coin sellerSecurityDeposit = offer.getSellerSecurityDeposit();
|
||||
Coin tradeAmount = contract.getTradeAmount();
|
||||
CoreDisputesService.DisputePayout payout;
|
||||
if (selectedTradeAmountToggle == buyerGetsTradeAmountRadioButton) {
|
||||
disputeResult.setBuyerPayoutAmount(tradeAmount.add(buyerSecurityDeposit));
|
||||
disputeResult.setSellerPayoutAmount(sellerSecurityDeposit);
|
||||
payout = CoreDisputesService.DisputePayout.BUYER_GETS_TRADE_AMOUNT;
|
||||
disputeResult.setWinner(DisputeResult.Winner.BUYER);
|
||||
} else if (selectedTradeAmountToggle == buyerGetsAllRadioButton) {
|
||||
disputeResult.setBuyerPayoutAmount(tradeAmount
|
||||
.add(buyerSecurityDeposit)
|
||||
.add(sellerSecurityDeposit)); // TODO (woodser): apply min payout to incentivize loser (see post v1.1.7)
|
||||
disputeResult.setSellerPayoutAmount(Coin.ZERO);
|
||||
payout = CoreDisputesService.DisputePayout.BUYER_GETS_ALL;
|
||||
disputeResult.setWinner(DisputeResult.Winner.BUYER);
|
||||
} else if (selectedTradeAmountToggle == sellerGetsTradeAmountRadioButton) {
|
||||
disputeResult.setBuyerPayoutAmount(buyerSecurityDeposit);
|
||||
disputeResult.setSellerPayoutAmount(tradeAmount.add(sellerSecurityDeposit));
|
||||
payout = CoreDisputesService.DisputePayout.SELLER_GETS_TRADE_AMOUNT;
|
||||
disputeResult.setWinner(DisputeResult.Winner.SELLER);
|
||||
} else if (selectedTradeAmountToggle == sellerGetsAllRadioButton) {
|
||||
disputeResult.setBuyerPayoutAmount(Coin.ZERO);
|
||||
disputeResult.setSellerPayoutAmount(tradeAmount
|
||||
.add(sellerSecurityDeposit)
|
||||
.add(buyerSecurityDeposit));
|
||||
payout = CoreDisputesService.DisputePayout.SELLER_GETS_ALL;
|
||||
disputeResult.setWinner(DisputeResult.Winner.SELLER);
|
||||
} else {
|
||||
// should not happen
|
||||
throw new IllegalStateException("Unknown radio button");
|
||||
}
|
||||
|
||||
disputesService.applyPayoutAmountsToDisputeResult(payout, dispute, disputeResult, -1);
|
||||
buyerPayoutAmountInputTextField.setText(formatter.formatCoin(disputeResult.getBuyerPayoutAmount()));
|
||||
sellerPayoutAmountInputTextField.setText(formatter.formatCoin(disputeResult.getSellerPayoutAmount()));
|
||||
}
|
||||
|
|
|
@ -204,8 +204,8 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> {
|
|||
rows++;
|
||||
if (trade.getPayoutTx() != null)
|
||||
rows++;
|
||||
boolean showDisputedTx = arbitrationManager.findOwnDispute(trade.getId()).isPresent() &&
|
||||
arbitrationManager.findOwnDispute(trade.getId()).get().getDisputePayoutTxId() != null;
|
||||
boolean showDisputedTx = arbitrationManager.findDispute(trade.getId()).isPresent() &&
|
||||
arbitrationManager.findDispute(trade.getId()).get().getDisputePayoutTxId() != null;
|
||||
if (showDisputedTx)
|
||||
rows++;
|
||||
if (trade.hasFailed())
|
||||
|
@ -301,7 +301,7 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> {
|
|||
trade.getPayoutTx().getHash());
|
||||
if (showDisputedTx)
|
||||
addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("tradeDetailsWindow.disputedPayoutTxId"),
|
||||
arbitrationManager.findOwnDispute(trade.getId()).get().getDisputePayoutTxId());
|
||||
arbitrationManager.findDispute(trade.getId()).get().getDisputePayoutTxId());
|
||||
|
||||
if (trade.hasFailed()) {
|
||||
textArea = addConfirmationLabelTextArea(gridPane, ++rowIndex, Res.get("shared.errorMessage"), "", 0).second;
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
package bisq.desktop.main.overlays.windows;
|
||||
|
||||
import bisq.desktop.main.overlays.Overlay;
|
||||
import bisq.desktop.main.support.dispute.DisputeSummaryVerification;
|
||||
import bisq.core.support.dispute.DisputeSummaryVerification;
|
||||
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.support.dispute.mediation.mediator.MediatorManager;
|
||||
|
|
|
@ -29,6 +29,7 @@ import bisq.desktop.main.support.dispute.client.mediation.MediationClientView;
|
|||
import bisq.desktop.util.GUIUtil;
|
||||
|
||||
import bisq.core.account.witness.AccountAgeWitnessService;
|
||||
import bisq.core.api.CoreDisputesService;
|
||||
import bisq.core.api.CoreMoneroConnectionsService;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.locale.Res;
|
||||
|
@ -122,6 +123,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
private Trade selectedTrade;
|
||||
@Getter
|
||||
private final PubKeyRingProvider pubKeyRingProvider;
|
||||
private final CoreDisputesService disputesService;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor, initialization
|
||||
|
@ -141,7 +143,8 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
Navigation navigation,
|
||||
WalletPasswordWindow walletPasswordWindow,
|
||||
NotificationCenter notificationCenter,
|
||||
OfferUtil offerUtil) {
|
||||
OfferUtil offerUtil,
|
||||
CoreDisputesService disputesService) {
|
||||
this.tradeManager = tradeManager;
|
||||
this.xmrWalletService = xmrWalletService;
|
||||
this.pubKeyRingProvider = pubKeyRingProvider;
|
||||
|
@ -156,6 +159,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
this.walletPasswordWindow = walletPasswordWindow;
|
||||
this.notificationCenter = notificationCenter;
|
||||
this.offerUtil = offerUtil;
|
||||
this.disputesService = disputesService;
|
||||
|
||||
tradesListChangeListener = change -> onListChanged();
|
||||
notificationCenter.setSelectItemByTradeIdConsumer(this::selectItemByTradeId);
|
||||
|
@ -544,40 +548,12 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
} else if (useArbitration) {
|
||||
// Only if we have completed mediation we allow arbitration
|
||||
disputeManager = arbitrationManager;
|
||||
PubKeyRing arbitratorPubKeyRing = trade.getArbitratorPubKeyRing();
|
||||
checkNotNull(arbitratorPubKeyRing, "arbitratorPubKeyRing must not be null");
|
||||
byte[] depositTxSerialized = null; // depositTx.bitcoinSerialize(); TODO (woodser)
|
||||
String depositTxHashAsString = null; // depositTx.getHashAsString(); TODO (woodser)
|
||||
Dispute dispute = new Dispute(new Date().getTime(),
|
||||
trade.getId(),
|
||||
pubKeyRingProvider.get().hashCode(), // trader id,
|
||||
true,
|
||||
(offer.getDirection() == OfferPayload.Direction.BUY) == isMaker,
|
||||
isMaker,
|
||||
pubKeyRingProvider.get(),
|
||||
trade.getDate().getTime(),
|
||||
trade.getMaxTradePeriodDate().getTime(),
|
||||
trade.getContract(),
|
||||
trade.getContractHash(),
|
||||
depositTxSerialized,
|
||||
payoutTxSerialized,
|
||||
depositTxHashAsString,
|
||||
payoutTxHashAsString,
|
||||
trade.getContractAsJson(),
|
||||
trade.getMaker().getContractSignature(),
|
||||
trade.getTaker().getContractSignature(),
|
||||
trade.getMaker().getPaymentAccountPayload(),
|
||||
trade.getTaker().getPaymentAccountPayload(),
|
||||
arbitratorPubKeyRing,
|
||||
isSupportTicket,
|
||||
SupportType.ARBITRATION);
|
||||
|
||||
trade.setDisputeState(Trade.DisputeState.DISPUTE_REQUESTED);
|
||||
Dispute dispute = disputesService.createDisputeForTrade(trade, offer, pubKeyRingProvider.get(), isMaker, isSupportTicket);
|
||||
sendOpenNewDisputeMessage(dispute, false, disputeManager, updatedMultisigHex);
|
||||
tradeManager.requestPersistence();
|
||||
} else {
|
||||
log.warn("Invalid dispute state {}", disputeState.name());
|
||||
}
|
||||
tradeManager.requestPersistence();
|
||||
}
|
||||
|
||||
private void sendOpenNewDisputeMessage(Dispute dispute, boolean reOpen, DisputeManager<? extends DisputeList<Dispute>> disputeManager, String senderMultisigHex) {
|
||||
|
|
|
@ -486,7 +486,7 @@ public abstract class TradeStepView extends AnchorPane {
|
|||
}
|
||||
applyOnDisputeOpened();
|
||||
|
||||
ownDispute = model.dataModel.arbitrationManager.findOwnDispute(trade.getId());
|
||||
ownDispute = model.dataModel.arbitrationManager.findDispute(trade.getId());
|
||||
ownDispute.ifPresent(dispute -> {
|
||||
if (tradeStepInfo != null)
|
||||
tradeStepInfo.setState(TradeStepInfo.State.IN_ARBITRATION_SELF_REQUESTED);
|
||||
|
@ -499,7 +499,7 @@ public abstract class TradeStepView extends AnchorPane {
|
|||
}
|
||||
applyOnDisputeOpened();
|
||||
|
||||
ownDispute = model.dataModel.arbitrationManager.findOwnDispute(trade.getId());
|
||||
ownDispute = model.dataModel.arbitrationManager.findDispute(trade.getId());
|
||||
ownDispute.ifPresent(dispute -> {
|
||||
if (tradeStepInfo != null)
|
||||
tradeStepInfo.setState(TradeStepInfo.State.IN_ARBITRATION_PEER_REQUESTED);
|
||||
|
@ -513,7 +513,7 @@ public abstract class TradeStepView extends AnchorPane {
|
|||
}
|
||||
applyOnDisputeOpened();
|
||||
|
||||
ownDispute = model.dataModel.mediationManager.findOwnDispute(trade.getId());
|
||||
ownDispute = model.dataModel.mediationManager.findDispute(trade.getId());
|
||||
ownDispute.ifPresent(dispute -> {
|
||||
if (tradeStepInfo != null)
|
||||
tradeStepInfo.setState(TradeStepInfo.State.IN_MEDIATION_SELF_REQUESTED);
|
||||
|
@ -525,7 +525,7 @@ public abstract class TradeStepView extends AnchorPane {
|
|||
}
|
||||
applyOnDisputeOpened();
|
||||
|
||||
ownDispute = model.dataModel.mediationManager.findOwnDispute(trade.getId());
|
||||
ownDispute = model.dataModel.mediationManager.findDispute(trade.getId());
|
||||
ownDispute.ifPresent(dispute -> {
|
||||
if (tradeStepInfo != null) {
|
||||
tradeStepInfo.setState(TradeStepInfo.State.IN_MEDIATION_PEER_REQUESTED);
|
||||
|
|
|
@ -1,103 +0,0 @@
|
|||
/*
|
||||
* This file is part of Haveno.
|
||||
*
|
||||
* Haveno is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Haveno is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.desktop.main.support.dispute;
|
||||
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.support.dispute.Dispute;
|
||||
import bisq.core.support.dispute.DisputeList;
|
||||
import bisq.core.support.dispute.DisputeManager;
|
||||
import bisq.core.support.dispute.DisputeResult;
|
||||
import bisq.core.support.dispute.agent.DisputeAgent;
|
||||
import bisq.core.support.dispute.mediation.mediator.MediatorManager;
|
||||
import bisq.core.support.dispute.refund.refundagent.RefundAgentManager;
|
||||
|
||||
import bisq.network.p2p.NodeAddress;
|
||||
|
||||
import bisq.common.crypto.CryptoException;
|
||||
import bisq.common.crypto.Hash;
|
||||
import bisq.common.crypto.Sig;
|
||||
import bisq.common.util.Utilities;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.PublicKey;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
@Slf4j
|
||||
public class DisputeSummaryVerification {
|
||||
// Must not change as it is used for splitting the text for verifying the signature of the summary message
|
||||
private static final String SEPARATOR1 = "\n-----BEGIN SIGNATURE-----\n";
|
||||
private static final String SEPARATOR2 = "\n-----END SIGNATURE-----\n";
|
||||
|
||||
public static String signAndApply(DisputeManager<? extends DisputeList<Dispute>> disputeManager,
|
||||
DisputeResult disputeResult,
|
||||
String textToSign) {
|
||||
|
||||
byte[] hash = Hash.getSha256Hash(textToSign);
|
||||
KeyPair signatureKeyPair = disputeManager.getSignatureKeyPair();
|
||||
String sigAsHex;
|
||||
try {
|
||||
byte[] signature = Sig.sign(signatureKeyPair.getPrivate(), hash);
|
||||
sigAsHex = Utilities.encodeToHex(signature);
|
||||
disputeResult.setArbitratorSignature(signature);
|
||||
} catch (CryptoException e) {
|
||||
sigAsHex = "Signing failed";
|
||||
}
|
||||
|
||||
return Res.get("disputeSummaryWindow.close.msgWithSig",
|
||||
textToSign,
|
||||
SEPARATOR1,
|
||||
sigAsHex,
|
||||
SEPARATOR2);
|
||||
}
|
||||
|
||||
public static String verifySignature(String input,
|
||||
MediatorManager mediatorManager,
|
||||
RefundAgentManager refundAgentManager) {
|
||||
try {
|
||||
String[] parts = input.split(SEPARATOR1);
|
||||
String textToSign = parts[0];
|
||||
String fullAddress = textToSign.split("\n")[1].split(": ")[1];
|
||||
NodeAddress nodeAddress = new NodeAddress(fullAddress);
|
||||
DisputeAgent disputeAgent = mediatorManager.getDisputeAgentByNodeAddress(nodeAddress).orElse(null);
|
||||
if (disputeAgent == null) {
|
||||
disputeAgent = refundAgentManager.getDisputeAgentByNodeAddress(nodeAddress).orElse(null);
|
||||
}
|
||||
checkNotNull(disputeAgent);
|
||||
PublicKey pubKey = disputeAgent.getPubKeyRing().getSignaturePubKey();
|
||||
|
||||
String sigString = parts[1].split(SEPARATOR2)[0];
|
||||
byte[] sig = Utilities.decodeFromHex(sigString);
|
||||
byte[] hash = Hash.getSha256Hash(textToSign);
|
||||
try {
|
||||
boolean result = Sig.verify(pubKey, hash, sig);
|
||||
if (result) {
|
||||
return Res.get("support.sigCheck.popup.success");
|
||||
} else {
|
||||
return Res.get("support.sigCheck.popup.failed");
|
||||
}
|
||||
} catch (CryptoException e) {
|
||||
return Res.get("support.sigCheck.popup.failed");
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
return Res.get("support.sigCheck.popup.invalidFormat");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue