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

@ -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();
@ -163,7 +164,8 @@ public class BuyerPreparePaymentSentMessage extends TradeTask {
// 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));
@ -203,5 +205,3 @@ public class BuyerPreparePaymentSentMessage extends TradeTask {
return sb.toString(); 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();