re-export multisig hex on create multisig tx or open dispute

This commit is contained in:
woodser 2024-07-31 19:39:54 -04:00
parent d4a9838cd8
commit 79cd9f3e82
9 changed files with 120 additions and 104 deletions

View File

@ -112,7 +112,7 @@ public class CoreDisputesService {
// Sends the openNewDisputeMessage to arbitrator, who will then create 2 disputes // Sends the openNewDisputeMessage to arbitrator, who will then create 2 disputes
// one for the opener, the other for the peer, see sendPeerOpenedDisputeMessage. // one for the opener, the other for the peer, see sendPeerOpenedDisputeMessage.
disputeManager.sendDisputeOpenedMessage(dispute, false, trade.getSelf().getUpdatedMultisigHex(), resultHandler, faultHandler); disputeManager.sendDisputeOpenedMessage(dispute, false, resultHandler, faultHandler);
tradeManager.requestPersistence(); tradeManager.requestPersistence();
}, trade.getId()); }, trade.getId());
} }

View File

@ -157,6 +157,11 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
disputeListService.requestPersistence(); disputeListService.requestPersistence();
} }
protected void requestPersistence(Trade trade) {
trade.requestPersistence();
disputeListService.requestPersistence();
}
@Override @Override
public NodeAddress getPeerNodeAddress(ChatMessage message) { public NodeAddress getPeerNodeAddress(ChatMessage message) {
Optional<Dispute> disputeOptional = findDispute(message); Optional<Dispute> disputeOptional = findDispute(message);
@ -322,7 +327,6 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
// trader sends message to arbitrator to open dispute // trader sends message to arbitrator to open dispute
public void sendDisputeOpenedMessage(Dispute dispute, public void sendDisputeOpenedMessage(Dispute dispute,
boolean reOpen, boolean reOpen,
String updatedMultisigHex,
ResultHandler resultHandler, ResultHandler resultHandler,
FaultHandler faultHandler) { FaultHandler faultHandler) {
@ -372,12 +376,13 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
} }
// create dispute opened message // create dispute opened message
trade.exportMultisigHex();
NodeAddress agentNodeAddress = getAgentNodeAddress(dispute); NodeAddress agentNodeAddress = getAgentNodeAddress(dispute);
DisputeOpenedMessage disputeOpenedMessage = new DisputeOpenedMessage(dispute, DisputeOpenedMessage disputeOpenedMessage = new DisputeOpenedMessage(dispute,
p2PService.getAddress(), p2PService.getAddress(),
UUID.randomUUID().toString(), UUID.randomUUID().toString(),
getSupportType(), getSupportType(),
updatedMultisigHex, trade.getSelf().getUpdatedMultisigHex(),
trade.getArbitrator().getPaymentSentMessage()); trade.getArbitrator().getPaymentSentMessage());
log.info("Send {} to peer {}. tradeId={}, openNewDisputeMessage.uid={}, " + log.info("Send {} to peer {}. tradeId={}, openNewDisputeMessage.uid={}, " +
"chatMessage.uid={}", "chatMessage.uid={}",
@ -792,7 +797,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
disputeResult.getChatMessage().setArrived(true); disputeResult.getChatMessage().setArrived(true);
trade.advanceDisputeState(Trade.DisputeState.ARBITRATOR_SAW_ARRIVED_DISPUTE_CLOSED_MSG); trade.advanceDisputeState(Trade.DisputeState.ARBITRATOR_SAW_ARRIVED_DISPUTE_CLOSED_MSG);
trade.pollWalletNormallyForMs(30000); trade.pollWalletNormallyForMs(30000);
requestPersistence(); requestPersistence(trade);
resultHandler.handleResult(); resultHandler.handleResult();
} }
@ -811,7 +816,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
disputeResult.getChatMessage().setStoredInMailbox(true); disputeResult.getChatMessage().setStoredInMailbox(true);
Trade trade = tradeManager.getTrade(dispute.getTradeId()); Trade trade = tradeManager.getTrade(dispute.getTradeId());
trade.advanceDisputeState(Trade.DisputeState.ARBITRATOR_STORED_IN_MAILBOX_DISPUTE_CLOSED_MSG); trade.advanceDisputeState(Trade.DisputeState.ARBITRATOR_STORED_IN_MAILBOX_DISPUTE_CLOSED_MSG);
requestPersistence(); requestPersistence(trade);
resultHandler.handleResult(); resultHandler.handleResult();
} }
@ -828,13 +833,13 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
// the state, as that is displayed to the user and we only persist that msg // the state, as that is displayed to the user and we only persist that msg
disputeResult.getChatMessage().setSendMessageError(errorMessage); disputeResult.getChatMessage().setSendMessageError(errorMessage);
trade.advanceDisputeState(Trade.DisputeState.ARBITRATOR_SEND_FAILED_DISPUTE_CLOSED_MSG); trade.advanceDisputeState(Trade.DisputeState.ARBITRATOR_SEND_FAILED_DISPUTE_CLOSED_MSG);
requestPersistence(); requestPersistence(trade);
faultHandler.handleFault(errorMessage, new RuntimeException(errorMessage)); faultHandler.handleFault(errorMessage, new RuntimeException(errorMessage));
} }
} }
); );
trade.advanceDisputeState(Trade.DisputeState.ARBITRATOR_SENT_DISPUTE_CLOSED_MSG); trade.advanceDisputeState(Trade.DisputeState.ARBITRATOR_SENT_DISPUTE_CLOSED_MSG);
requestPersistence(); requestPersistence(trade);
} catch (Exception e) { } catch (Exception e) {
faultHandler.handleFault(e.getMessage(), e); faultHandler.handleFault(e.getMessage(), e);
} }
@ -900,11 +905,11 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
// update trade state // update trade state
if (updateState) { if (updateState) {
trade.getProcessModel().setUnsignedPayoutTx(payoutTx); trade.getProcessModel().setUnsignedPayoutTx(payoutTx);
trade.getSelf().setUpdatedMultisigHex(trade.getWallet().exportMultisigHex());
trade.updatePayout(payoutTx); trade.updatePayout(payoutTx);
if (trade.getBuyer().getUpdatedMultisigHex() != null && trade.getBuyer().getUnsignedPayoutTxHex() == null) trade.getBuyer().setUnsignedPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex()); if (trade.getBuyer().getUpdatedMultisigHex() != null && trade.getBuyer().getUnsignedPayoutTxHex() == null) trade.getBuyer().setUnsignedPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex());
if (trade.getSeller().getUpdatedMultisigHex() != null && trade.getSeller().getUnsignedPayoutTxHex() == null) trade.getSeller().setUnsignedPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex()); if (trade.getSeller().getUpdatedMultisigHex() != null && trade.getSeller().getUnsignedPayoutTxHex() == null) trade.getSeller().setUnsignedPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex());
} }
trade.requestPersistence();
return payoutTx; return payoutTx;
} catch (Exception e) { } catch (Exception e) {
trade.syncAndPollWallet(); trade.syncAndPollWallet();

View File

@ -252,7 +252,7 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
// save dispute closed message for reprocessing // save dispute closed message for reprocessing
trade.getArbitrator().setDisputeClosedMessage(disputeClosedMessage); trade.getArbitrator().setDisputeClosedMessage(disputeClosedMessage);
requestPersistence(); requestPersistence(trade);
// verify arbitrator does not receive DisputeClosedMessage // verify arbitrator does not receive DisputeClosedMessage
if (keyRing.getPubKeyRing().equals(dispute.getAgentPubKeyRing())) { if (keyRing.getPubKeyRing().equals(dispute.getAgentPubKeyRing())) {
@ -326,17 +326,17 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
// We use the chatMessage as we only persist those not the DisputeClosedMessage. // We use the chatMessage as we only persist those not the DisputeClosedMessage.
// If we would use the DisputeClosedMessage we could not lookup for the msg when we receive the AckMessage. // If we would use the DisputeClosedMessage we could not lookup for the msg when we receive the AckMessage.
sendAckMessage(chatMessage, dispute.getAgentPubKeyRing(), true, null); sendAckMessage(chatMessage, dispute.getAgentPubKeyRing(), true, null);
requestPersistence(); requestPersistence(trade);
} catch (Exception e) { } catch (Exception e) {
log.warn("Error processing dispute closed message: " + e.getMessage()); log.warn("Error processing dispute closed message: " + e.getMessage());
e.printStackTrace(); e.printStackTrace();
requestPersistence(); requestPersistence(trade);
// nack bad message and do not reprocess // nack bad message and do not reprocess
if (e instanceof IllegalArgumentException) { if (e instanceof IllegalArgumentException) {
trade.getArbitrator().setDisputeClosedMessage(null); // message is processed trade.getArbitrator().setDisputeClosedMessage(null); // message is processed
sendAckMessage(chatMessage, dispute.getAgentPubKeyRing(), false, e.getMessage()); sendAckMessage(chatMessage, dispute.getAgentPubKeyRing(), false, e.getMessage());
requestPersistence(); requestPersistence(trade);
throw e; throw e;
} }
@ -447,7 +447,7 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
String signedMultisigTxHex = result.getSignedMultisigTxHex(); String signedMultisigTxHex = result.getSignedMultisigTxHex();
disputeTxSet.setMultisigTxHex(signedMultisigTxHex); disputeTxSet.setMultisigTxHex(signedMultisigTxHex);
trade.setPayoutTxHex(signedMultisigTxHex); trade.setPayoutTxHex(signedMultisigTxHex);
requestPersistence(); requestPersistence(trade);
// verify mining fee is within tolerance by recreating payout tx // verify mining fee is within tolerance by recreating payout tx
// TODO (monero-project): creating tx will require exchanging updated multisig hex if message needs reprocessed. provide weight with describe_transfer so fee can be estimated? // TODO (monero-project): creating tx will require exchanging updated multisig hex if message needs reprocessed. provide weight with describe_transfer so fee can be estimated?
@ -487,6 +487,7 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
trade.updatePayout(disputeTxSet.getTxs().get(0)); trade.updatePayout(disputeTxSet.getTxs().get(0));
trade.setPayoutState(Trade.PayoutState.PAYOUT_PUBLISHED); trade.setPayoutState(Trade.PayoutState.PAYOUT_PUBLISHED);
dispute.setDisputePayoutTxId(disputeTxSet.getTxs().get(0).getHash()); dispute.setDisputePayoutTxId(disputeTxSet.getTxs().get(0).getHash());
requestPersistence(trade);
return disputeTxSet; return disputeTxSet;
} }

View File

@ -1058,11 +1058,21 @@ public abstract class Trade implements Tradable, Model {
public MoneroTxWallet createTx(MoneroTxConfig txConfig) { public MoneroTxWallet createTx(MoneroTxConfig txConfig) {
synchronized (walletLock) { synchronized (walletLock) {
synchronized (HavenoUtils.getWalletFunctionLock()) { synchronized (HavenoUtils.getWalletFunctionLock()) {
return wallet.createTx(txConfig); MoneroTxWallet tx = wallet.createTx(txConfig);
exportMultisigHex();
requestSaveWallet();
return tx;
} }
} }
} }
public void exportMultisigHex() {
synchronized (walletLock) {
getSelf().setUpdatedMultisigHex(wallet.exportMultisigHex());
requestPersistence();
}
}
public void importMultisigHex() { public void importMultisigHex() {
synchronized (walletLock) { synchronized (walletLock) {
synchronized (HavenoUtils.getDaemonLock()) { // lock on daemon because import calls full refresh synchronized (HavenoUtils.getDaemonLock()) { // lock on daemon because import calls full refresh
@ -1220,13 +1230,11 @@ public abstract class Trade implements Tradable, Model {
.setPriority(XmrWalletService.PROTOCOL_FEE_PRIORITY)); .setPriority(XmrWalletService.PROTOCOL_FEE_PRIORITY));
// update state // update state
saveWallet();
BigInteger payoutTxFeeSplit = payoutTx.getFee().divide(BigInteger.valueOf(2)); BigInteger payoutTxFeeSplit = payoutTx.getFee().divide(BigInteger.valueOf(2));
getBuyer().setPayoutTxFee(payoutTxFeeSplit); getBuyer().setPayoutTxFee(payoutTxFeeSplit);
getBuyer().setPayoutAmount(HavenoUtils.getDestination(buyerPayoutAddress, payoutTx).getAmount()); getBuyer().setPayoutAmount(HavenoUtils.getDestination(buyerPayoutAddress, payoutTx).getAmount());
getSeller().setPayoutTxFee(payoutTxFeeSplit); getSeller().setPayoutTxFee(payoutTxFeeSplit);
getSeller().setPayoutAmount(HavenoUtils.getDestination(sellerPayoutAddress, payoutTx).getAmount()); getSeller().setPayoutAmount(HavenoUtils.getDestination(sellerPayoutAddress, payoutTx).getAmount());
getSelf().setUpdatedMultisigHex(wallet.exportMultisigHex());
return payoutTx; return payoutTx;
} }
@ -1273,6 +1281,9 @@ public abstract class Trade implements Tradable, Model {
if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e; if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e;
if (xmrConnectionService.isConnected()) requestSwitchToNextBestConnection(); if (xmrConnectionService.isConnected()) requestSwitchToNextBestConnection();
HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying
} finally {
requestSaveWallet();
requestPersistence();
} }
} }
} }

View File

@ -64,9 +64,9 @@ public class BuyerPreparePaymentSentMessage extends TradeTask {
// skip if payout tx already created // skip if payout tx already created
if (trade.getSelf().getUnsignedPayoutTxHex() != null) { if (trade.getSelf().getUnsignedPayoutTxHex() != null) {
log.warn("Skipping preparation of payment sent message because payout tx is already created for {} {}", trade.getClass().getSimpleName(), trade.getShortId()); log.warn("Skipping preparation of payment sent message because payout tx is already created for {} {}", trade.getClass().getSimpleName(), trade.getShortId());
complete(); complete();
return; return;
} }
// validate state // validate state
@ -87,6 +87,7 @@ public class BuyerPreparePaymentSentMessage extends TradeTask {
MoneroTxWallet payoutTx = trade.createPayoutTx(); MoneroTxWallet payoutTx = trade.createPayoutTx();
trade.updatePayout(payoutTx); trade.updatePayout(payoutTx);
trade.getSelf().setUnsignedPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex()); trade.getSelf().setUnsignedPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex());
trade.requestPersistence();
} }
complete(); complete();
@ -107,101 +108,100 @@ public class BuyerPreparePaymentSentMessage extends TradeTask {
*/ */
public static class Pair<F, S> { public static class Pair<F, S> {
private F first; private F first;
private S second; private S second;
public Pair(F first, S second) { public Pair(F first, S second) {
super(); super();
this.first = first; this.first = first;
this.second = second; this.second = second;
} }
public F getFirst() { public F getFirst() {
return first; return first;
} }
public void setFirst(F first) { public void setFirst(F first) {
this.first = first; this.first = first;
} }
public S getSecond() { public S getSecond() {
return second; return second;
} }
public void setSecond(S second) { public void setSecond(S second) {
this.second = second; this.second = second;
} }
} }
public static void printBalances(MoneroWallet wallet) { public static void printBalances(MoneroWallet wallet) {
// collect info about subaddresses // collect info about subaddresses
List<Pair<String, List<Object>>> pairs = new ArrayList<Pair<String, List<Object>>>(); List<Pair<String, List<Object>>> pairs = new ArrayList<Pair<String, List<Object>>>();
//if (wallet == null) wallet = TestUtils.getWalletJni(); //if (wallet == null) wallet = TestUtils.getWalletJni();
BigInteger balance = wallet.getBalance(); BigInteger balance = wallet.getBalance();
BigInteger unlockedBalance = wallet.getUnlockedBalance(); BigInteger unlockedBalance = wallet.getUnlockedBalance();
List<MoneroAccount> accounts = wallet.getAccounts(true); List<MoneroAccount> accounts = wallet.getAccounts(true);
System.out.println("Wallet balance: " + balance); System.out.println("Wallet balance: " + balance);
System.out.println("Wallet unlocked balance: " + unlockedBalance); System.out.println("Wallet unlocked balance: " + unlockedBalance);
for (MoneroAccount account : accounts) { for (MoneroAccount account : accounts) {
add(pairs, "ACCOUNT", account.getIndex()); add(pairs, "ACCOUNT", account.getIndex());
add(pairs, "SUBADDRESS", ""); add(pairs, "SUBADDRESS", "");
add(pairs, "LABEL", ""); add(pairs, "LABEL", "");
add(pairs, "ADDRESS", ""); add(pairs, "ADDRESS", "");
add(pairs, "BALANCE", account.getBalance()); add(pairs, "BALANCE", account.getBalance());
add(pairs, "UNLOCKED", account.getUnlockedBalance()); add(pairs, "UNLOCKED", account.getUnlockedBalance());
for (MoneroSubaddress subaddress : account.getSubaddresses()) { for (MoneroSubaddress subaddress : account.getSubaddresses()) {
add(pairs, "ACCOUNT", account.getIndex()); add(pairs, "ACCOUNT", account.getIndex());
add(pairs, "SUBADDRESS", subaddress.getIndex()); add(pairs, "SUBADDRESS", subaddress.getIndex());
add(pairs, "LABEL", subaddress.getLabel()); add(pairs, "LABEL", subaddress.getLabel());
add(pairs, "ADDRESS", subaddress.getAddress()); add(pairs, "ADDRESS", subaddress.getAddress());
add(pairs, "BALANCE", subaddress.getBalance()); add(pairs, "BALANCE", subaddress.getBalance());
add(pairs, "UNLOCKED", subaddress.getUnlockedBalance()); add(pairs, "UNLOCKED", subaddress.getUnlockedBalance());
}
} }
}
// convert info to csv // convert info to csv
Integer length = null; Integer length = null;
for (Pair<String, List<Object>> pair : pairs) { for (Pair<String, List<Object>> pair : pairs) {
if (length == null) length = pair.getSecond().size(); if (length == null)
} length = pair.getSecond().size();
}
System.out.println(pairsToCsv(pairs)); System.out.println(pairsToCsv(pairs));
} }
private static void add(List<Pair<String, List<Object>>> pairs, String header, Object value) { private static void add(List<Pair<String, List<Object>>> pairs, String header, Object value) {
if (value == null) value = ""; if (value == null) value = "";
Pair<String, List<Object>> pair = null; Pair<String, List<Object>> pair = null;
for (Pair<String, List<Object>> aPair : pairs) { for (Pair<String, List<Object>> aPair : pairs) {
if (aPair.getFirst().equals(header)) { if (aPair.getFirst().equals(header)) {
pair = aPair; pair = aPair;
break; break;
}
} }
} if (pair == null) {
if (pair == null) { List<Object> vals = new ArrayList<Object>();
List<Object> vals = new ArrayList<Object>(); pair = new Pair<String, List<Object>>(header, vals);
pair = new Pair<String, List<Object>>(header, vals); pairs.add(pair);
pairs.add(pair); }
} pair.getSecond().add(value);
pair.getSecond().add(value);
} }
private static String pairsToCsv(List<Pair<String, List<Object>>> pairs) { private static String pairsToCsv(List<Pair<String, List<Object>>> pairs) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (int i = 0; i < pairs.size(); i++) { for (int i = 0; i < pairs.size(); i++) {
sb.append(pairs.get(i).getFirst()); sb.append(pairs.get(i).getFirst());
if (i < pairs.size() - 1) sb.append(','); if (i < pairs.size() - 1) sb.append(',');
else sb.append('\n'); else sb.append('\n');
}
for (int i = 0; i < pairs.get(0).getSecond().size(); i++) {
for (int j = 0; j < pairs.size(); j++) {
sb.append(pairs.get(j).getSecond().get(i));
if (j < pairs.size() - 1) sb.append(',');
else sb.append('\n');
} }
} for (int i = 0; i < pairs.get(0).getSecond().size(); i++) {
return sb.toString(); for (int j = 0; j < pairs.size(); j++) {
sb.append(pairs.get(j).getSecond().get(i));
if (j < pairs.size() - 1) sb.append(',');
else sb.append('\n');
}
}
return sb.toString();
} }
} }

View File

@ -58,7 +58,6 @@ public class ProcessPaymentSentMessage extends TradeTask {
// if seller, decrypt buyer's payment account payload // if seller, decrypt buyer's payment account payload
if (trade.isSeller()) trade.decryptPeerPaymentAccountPayload(message.getPaymentAccountKey()); if (trade.isSeller()) trade.decryptPeerPaymentAccountPayload(message.getPaymentAccountKey());
trade.requestPersistence();
// update state // update state
trade.advanceState(Trade.State.BUYER_SENT_PAYMENT_SENT_MSG); trade.advanceState(Trade.State.BUYER_SENT_PAYMENT_SENT_MSG);

View File

@ -90,7 +90,7 @@ public class SellerPreparePaymentReceivedMessage extends TradeTask {
for (Dispute dispute : trade.getDisputes()) dispute.setIsClosed(); for (Dispute dispute : trade.getDisputes()) dispute.setIsClosed();
} }
processModel.getTradeManager().requestPersistence(); trade.requestPersistence();
complete(); complete();
} catch (Throwable t) { } catch (Throwable t) {
failed(t); failed(t);

View File

@ -76,8 +76,7 @@ public abstract class SendDepositsConfirmedMessage extends SendMailboxMessageTas
// export multisig hex once // export multisig hex once
if (trade.getSelf().getUpdatedMultisigHex() == null) { if (trade.getSelf().getUpdatedMultisigHex() == null) {
trade.getSelf().setUpdatedMultisigHex(trade.getWallet().exportMultisigHex()); trade.exportMultisigHex();
processModel.getTradeManager().requestPersistence();
} }
// We do not use a real unique ID here as we want to be able to re-send the exact same message in case the // We do not use a real unique ID here as we want to be able to re-send the exact same message in case the

View File

@ -545,27 +545,28 @@ public class PendingTradesDataModel extends ActivatableDataModel {
dispute.setExtraData("counterCurrencyExtraData", trade.getCounterCurrencyExtraData()); dispute.setExtraData("counterCurrencyExtraData", trade.getCounterCurrencyExtraData());
trade.setDisputeState(Trade.DisputeState.MEDIATION_REQUESTED); trade.setDisputeState(Trade.DisputeState.MEDIATION_REQUESTED);
sendDisputeOpenedMessage(dispute, false, disputeManager, trade.getSelf().getUpdatedMultisigHex()); sendDisputeOpenedMessage(dispute, false, disputeManager);
tradeManager.requestPersistence(); tradeManager.requestPersistence();
} else if (useArbitration) { } else if (useArbitration) {
// Only if we have completed mediation we allow arbitration // Only if we have completed mediation we allow arbitration
disputeManager = arbitrationManager; disputeManager = arbitrationManager;
Dispute dispute = disputesService.createDisputeForTrade(trade, offer, pubKeyRingProvider.get(), isMaker, isSupportTicket); Dispute dispute = disputesService.createDisputeForTrade(trade, offer, pubKeyRingProvider.get(), isMaker, isSupportTicket);
sendDisputeOpenedMessage(dispute, false, disputeManager, trade.getSelf().getUpdatedMultisigHex()); trade.exportMultisigHex();
sendDisputeOpenedMessage(dispute, false, disputeManager);
tradeManager.requestPersistence(); tradeManager.requestPersistence();
} else { } else {
log.warn("Invalid dispute state {}", disputeState.name()); log.warn("Invalid dispute state {}", disputeState.name());
} }
} }
private void sendDisputeOpenedMessage(Dispute dispute, boolean reOpen, DisputeManager<? extends DisputeList<Dispute>> disputeManager, String senderMultisigHex) { private void sendDisputeOpenedMessage(Dispute dispute, boolean reOpen, DisputeManager<? extends DisputeList<Dispute>> disputeManager) {
disputeManager.sendDisputeOpenedMessage(dispute, reOpen, senderMultisigHex, disputeManager.sendDisputeOpenedMessage(dispute, reOpen,
() -> navigation.navigateTo(MainView.class, SupportView.class, ArbitrationClientView.class), (errorMessage, throwable) -> { () -> navigation.navigateTo(MainView.class, SupportView.class, ArbitrationClientView.class), (errorMessage, throwable) -> {
if ((throwable instanceof DisputeAlreadyOpenException)) { if ((throwable instanceof DisputeAlreadyOpenException)) {
errorMessage += "\n\n" + Res.get("portfolio.pending.openAgainDispute.msg"); errorMessage += "\n\n" + Res.get("portfolio.pending.openAgainDispute.msg");
new Popup().warning(errorMessage) new Popup().warning(errorMessage)
.actionButtonText(Res.get("portfolio.pending.openAgainDispute.button")) .actionButtonText(Res.get("portfolio.pending.openAgainDispute.button"))
.onAction(() -> sendDisputeOpenedMessage(dispute, true, disputeManager, senderMultisigHex)) .onAction(() -> sendDisputeOpenedMessage(dispute, true, disputeManager))
.closeButtonText(Res.get("shared.cancel")).show(); .closeButtonText(Res.get("shared.cancel")).show();
} else { } else {
new Popup().warning(errorMessage).show(); new Popup().warning(errorMessage).show();