mark dispute closed per ticket and sync for payout on ack

This commit is contained in:
woodser 2023-12-08 15:34:29 -05:00
parent 3730773006
commit 0c149b780f
4 changed files with 41 additions and 32 deletions

View file

@ -764,6 +764,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
disputeResult.getChatMessage().getUid()); disputeResult.getChatMessage().getUid());
clearPendingMessage(); clearPendingMessage();
dispute.setIsClosed();
// We use the chatMessage wrapped inside the DisputeClosedMessage for // We use the chatMessage wrapped inside the DisputeClosedMessage for
// 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().setArrived(true); disputeResult.getChatMessage().setArrived(true);
@ -782,6 +783,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
disputeResult.getChatMessage().getUid()); disputeResult.getChatMessage().getUid());
clearPendingMessage(); clearPendingMessage();
dispute.setIsClosed();
// We use the chatMessage wrapped inside the DisputeClosedMessage for // We use the chatMessage wrapped inside the DisputeClosedMessage for
// 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().setStoredInMailbox(true); disputeResult.getChatMessage().setStoredInMailbox(true);

View file

@ -392,8 +392,8 @@ public abstract class Trade implements Tradable, Model {
transient private Subscription tradePhaseSubscription; transient private Subscription tradePhaseSubscription;
transient private Subscription payoutStateSubscription; transient private Subscription payoutStateSubscription;
transient private TaskLooper txPollLooper; transient private TaskLooper txPollLooper;
transient private Long walletRefreshPeriod; transient private Long walletRefreshPeriodMs;
transient private Long syncNormalStartTime; transient private Long syncNormalStartTimeMs;
public static final long DEFER_PUBLISH_MS = 25000; // 25 seconds public static final long DEFER_PUBLISH_MS = 25000; // 25 seconds
private static final long IDLE_SYNC_PERIOD_MS = 1680000; // 28 minutes (monero's default connection timeout is 30 minutes on a local connection, so beyond this the wallets will disconnect) private static final long IDLE_SYNC_PERIOD_MS = 1680000; // 28 minutes (monero's default connection timeout is 30 minutes on a local connection, so beyond this the wallets will disconnect)
@ -775,7 +775,7 @@ public abstract class Trade implements Tradable, Model {
} }
public boolean isIdling() { public boolean isIdling() {
return this instanceof ArbitratorTrade && isDepositsConfirmed() && walletExists(); // arbitrator idles trade after deposits confirm return this instanceof ArbitratorTrade && isDepositsConfirmed() && walletExists() && syncNormalStartTimeMs == null; // arbitrator idles trade after deposits confirm unless overriden
} }
public void syncAndPollWallet() { public void syncAndPollWallet() {
@ -783,11 +783,25 @@ public abstract class Trade implements Tradable, Model {
} }
public void syncWalletNormallyForMs(long syncNormalDuration) { public void syncWalletNormallyForMs(long syncNormalDuration) {
syncNormalStartTime = System.currentTimeMillis(); syncNormalStartTimeMs = System.currentTimeMillis();
// override wallet refresh period
setWalletRefreshPeriod(xmrWalletService.getConnectionService().getRefreshPeriodMs()); setWalletRefreshPeriod(xmrWalletService.getConnectionService().getRefreshPeriodMs());
UserThread.runAfter(() -> {
if (!isShutDown && System.currentTimeMillis() >= syncNormalStartTime + syncNormalDuration) updateWalletRefreshPeriod(); // reset wallet refresh period after duration
}, syncNormalDuration); new Thread(() -> {
GenUtils.waitFor(syncNormalDuration);
if (!isShutDown && System.currentTimeMillis() >= syncNormalStartTimeMs + syncNormalDuration) {
syncNormalStartTimeMs = null;
updateWalletRefreshPeriod();
}
}).start();
// TODO: sync wallet because `auto_refresh` will not sync wallet until end of last sync period (which could be a long idle)
new Thread(() -> {
GenUtils.waitFor(1000);
if (!isShutDownStarted) trySyncWallet(true);
}).start();
} }
public void importMultisigHex() { public void importMultisigHex() {
@ -824,7 +838,7 @@ public abstract class Trade implements Tradable, Model {
stopPolling(); stopPolling();
xmrWalletService.closeWallet(wallet, true); xmrWalletService.closeWallet(wallet, true);
wallet = null; wallet = null;
walletRefreshPeriod = null; walletRefreshPeriodMs = null;
} }
} }
@ -1317,6 +1331,8 @@ public abstract class Trade implements Tradable, Model {
getSeller().setPayoutAmount(getSeller().getSecurityDeposit().subtract(getSeller().getPayoutTxFee())); getSeller().setPayoutAmount(getSeller().getSecurityDeposit().subtract(getSeller().getPayoutTxFee()));
} else if (getDisputeState().isClosed()) { } else if (getDisputeState().isClosed()) {
DisputeResult disputeResult = getDisputeResult(); DisputeResult disputeResult = getDisputeResult();
if (disputeResult == null) log.warn("Dispute result is not set for {} {}", getClass().getSimpleName(), getId());
else {
BigInteger[] buyerSellerPayoutTxFees = ArbitrationManager.getBuyerSellerPayoutTxCost(disputeResult, payoutTx.getFee()); BigInteger[] buyerSellerPayoutTxFees = ArbitrationManager.getBuyerSellerPayoutTxCost(disputeResult, payoutTx.getFee());
getBuyer().setPayoutTxFee(buyerSellerPayoutTxFees[0]); getBuyer().setPayoutTxFee(buyerSellerPayoutTxFees[0]);
getSeller().setPayoutTxFee(buyerSellerPayoutTxFees[1]); getSeller().setPayoutTxFee(buyerSellerPayoutTxFees[1]);
@ -1324,6 +1340,7 @@ public abstract class Trade implements Tradable, Model {
getSeller().setPayoutAmount(disputeResult.getSellerPayoutAmountBeforeCost().subtract(getSeller().getPayoutTxFee())); getSeller().setPayoutAmount(disputeResult.getSellerPayoutAmountBeforeCost().subtract(getSeller().getPayoutTxFee()));
} }
} }
}
public DisputeResult getDisputeResult() { public DisputeResult getDisputeResult() {
if (getDisputes().isEmpty()) return null; if (getDisputes().isEmpty()) return null;
@ -1834,11 +1851,11 @@ public abstract class Trade implements Tradable, Model {
setWalletRefreshPeriod(getWalletRefreshPeriod()); setWalletRefreshPeriod(getWalletRefreshPeriod());
} }
private void setWalletRefreshPeriod(long walletRefreshPeriod) { private void setWalletRefreshPeriod(long walletRefreshPeriodMs) {
synchronized (walletLock) { synchronized (walletLock) {
if (this.isShutDownStarted) return; if (this.isShutDownStarted) return;
if (this.walletRefreshPeriod != null && this.walletRefreshPeriod == walletRefreshPeriod) return; if (this.walletRefreshPeriodMs != null && this.walletRefreshPeriodMs == walletRefreshPeriodMs) return;
this.walletRefreshPeriod = walletRefreshPeriod; this.walletRefreshPeriodMs = walletRefreshPeriodMs;
if (getWallet() != null) { if (getWallet() != null) {
log.info("Setting wallet refresh rate for {} {} to {}", getClass().getSimpleName(), getId(), getWalletRefreshPeriod()); log.info("Setting wallet refresh rate for {} {} to {}", getClass().getSimpleName(), getId(), getWalletRefreshPeriod());
getWallet().startSyncing(getWalletRefreshPeriod()); // TODO (monero-project): wallet rpc waits until last sync period finishes before starting new sync period getWallet().startSyncing(getWalletRefreshPeriod()); // TODO (monero-project): wallet rpc waits until last sync period finishes before starting new sync period
@ -1855,7 +1872,7 @@ public abstract class Trade implements Tradable, Model {
if (isShutDownStarted || isPolling()) return; if (isShutDownStarted || isPolling()) return;
log.info("Starting to poll wallet for {} {}", getClass().getSimpleName(), getId()); log.info("Starting to poll wallet for {} {}", getClass().getSimpleName(), getId());
txPollLooper = new TaskLooper(() -> pollWallet()); txPollLooper = new TaskLooper(() -> pollWallet());
txPollLooper.start(walletRefreshPeriod); txPollLooper.start(walletRefreshPeriodMs);
} }
} }
@ -1961,6 +1978,7 @@ public abstract class Trade implements Tradable, Model {
} }
} catch (Exception e) { } catch (Exception e) {
if (!isShutDownStarted && wallet != null && isWalletConnected()) { if (!isShutDownStarted && wallet != null && isWalletConnected()) {
e.printStackTrace();
log.warn("Error polling trade wallet for {} {}: {}. Monerod={}", getClass().getSimpleName(), getId(), e.getMessage(), getXmrWalletService().getConnectionService().getConnection()); log.warn("Error polling trade wallet for {} {}: {}. Monerod={}", getClass().getSimpleName(), getId(), e.getMessage(), getXmrWalletService().getConnectionService().getConnection());
} }
} }

View file

@ -581,6 +581,8 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
Button cancelButton = tuple.second; Button cancelButton = tuple.second;
closeTicketButton.setOnAction(e -> { closeTicketButton.setOnAction(e -> {
closeTicketButton.disableProperty().unbind();
closeTicketButton.setDisable(true);
if (dispute.getSupportType() == SupportType.ARBITRATION && if (dispute.getSupportType() == SupportType.ARBITRATION &&
peersDisputeOptional.isPresent() && peersDisputeOptional.isPresent() &&
!peersDisputeOptional.get().isClosed() && !peersDisputeOptional.get().isClosed() &&

View file

@ -40,7 +40,6 @@ import haveno.core.support.messages.ChatMessage;
import haveno.core.trade.Contract; import haveno.core.trade.Contract;
import haveno.core.trade.HavenoUtils; import haveno.core.trade.HavenoUtils;
import haveno.core.trade.Trade; import haveno.core.trade.Trade;
import haveno.core.trade.Trade.DisputeState;
import haveno.core.trade.TradeManager; import haveno.core.trade.TradeManager;
import haveno.core.user.Preferences; import haveno.core.user.Preferences;
import haveno.core.util.FormattingUtils; import haveno.core.util.FormattingUtils;
@ -1353,7 +1352,7 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> {
// subscribe to trade's dispute state // subscribe to trade's dispute state
Trade trade = tradeManager.getTrade(item.getTradeId()); Trade trade = tradeManager.getTrade(item.getTradeId());
if (trade == null) log.warn("Dispute's trade is null for trade {}", item.getTradeId()); if (trade == null) log.warn("Dispute's trade is null for trade {}", item.getTradeId());
else subscription = EasyBind.subscribe(trade.disputeStateProperty(), disputeState -> setText(getDisputeStateText(disputeState))); else subscription = EasyBind.subscribe(item.isClosedProperty(), closedProp -> setText(getDisputeStateText(item)));
} else { } else {
if (closedProperty != null) { if (closedProperty != null) {
closedProperty.removeListener(listener); closedProperty.removeListener(listener);
@ -1373,28 +1372,16 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> {
return column; return column;
} }
private String getDisputeStateText(DisputeState disputeState) {
switch (disputeState) {
case DISPUTE_REQUESTED:
return Res.get("support.requested");
case DISPUTE_CLOSED:
return Res.get("support.closed");
default:
return Res.get("support.open");
}
}
private String getDisputeStateText(Dispute dispute) { private String getDisputeStateText(Dispute dispute) {
Trade trade = tradeManager.getTrade(dispute.getTradeId()); Trade trade = tradeManager.getTrade(dispute.getTradeId());
if (trade == null) { if (trade == null) {
log.warn("Dispute's trade is null for trade {}", dispute.getTradeId()); log.warn("Dispute's trade is null for trade {}", dispute.getTradeId());
return Res.get("support.closed"); return Res.get("support.closed");
} }
if (dispute.isClosed()) return Res.get("support.closed");
switch (trade.getDisputeState()) { switch (trade.getDisputeState()) {
case DISPUTE_REQUESTED: case DISPUTE_REQUESTED:
return Res.get("support.requested"); return Res.get("support.requested");
case DISPUTE_CLOSED:
return Res.get("support.closed");
default: default:
return Res.get("support.open"); return Res.get("support.open");
} }