improve error handling when clones taken at the same time

This commit is contained in:
woodser 2025-04-19 22:28:32 -04:00 committed by GitHub
parent c7a3a9740f
commit cf9a37f295
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 46 additions and 24 deletions

View file

@ -145,6 +145,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
private static final long DELETE_AFTER_MS = TradeProtocol.TRADE_STEP_TIMEOUT_SECONDS; private static final long DELETE_AFTER_MS = TradeProtocol.TRADE_STEP_TIMEOUT_SECONDS;
private static final int NUM_CONFIRMATIONS_FOR_SCHEDULED_IMPORT = 5; private static final int NUM_CONFIRMATIONS_FOR_SCHEDULED_IMPORT = 5;
protected final Object pollLock = new Object(); protected final Object pollLock = new Object();
private final Object removeTradeOnErrorLock = new Object();
protected static final Object importMultisigLock = new Object(); protected static final Object importMultisigLock = new Object();
private boolean pollInProgress; private boolean pollInProgress;
private boolean restartInProgress; private boolean restartInProgress;
@ -1608,11 +1609,12 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
} }
// shut down trade threads // shut down trade threads
isInitialized = false;
isShutDown = true; isShutDown = true;
List<Runnable> shutDownThreads = new ArrayList<>(); List<Runnable> shutDownThreads = new ArrayList<>();
shutDownThreads.add(() -> ThreadUtils.shutDown(getId())); shutDownThreads.add(() -> ThreadUtils.shutDown(getId()));
ThreadUtils.awaitTasks(shutDownThreads); ThreadUtils.awaitTasks(shutDownThreads);
stopProtocolTimeout();
isInitialized = false;
// save and close // save and close
if (wallet != null) { if (wallet != null) {
@ -1765,25 +1767,31 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
} }
private void removeTradeOnError() { private void removeTradeOnError() {
synchronized (removeTradeOnErrorLock) {
// skip if already shut down or removed
if (isShutDown || !processModel.getTradeManager().hasTrade(getId())) return;
log.warn("removeTradeOnError() trade={}, tradeId={}, state={}", getClass().getSimpleName(), getShortId(), getState()); log.warn("removeTradeOnError() trade={}, tradeId={}, state={}", getClass().getSimpleName(), getShortId(), getState());
// force close and re-open wallet in case stuck // force close and re-open wallet in case stuck
forceCloseWallet(); forceCloseWallet();
if (isDepositRequested()) getWallet(); if (isDepositRequested()) getWallet();
// clear and shut down trade
onShutDownStarted();
clearAndShutDown();
// shut down trade thread // shut down trade thread
try { try {
ThreadUtils.shutDown(getId(), 1000l); ThreadUtils.shutDown(getId(), 5000l);
} catch (Exception e) { } catch (Exception e) {
log.warn("Error shutting down trade thread for {} {}: {}", getClass().getSimpleName(), getId(), e.getMessage()); log.warn("Error shutting down trade thread for {} {}: {}", getClass().getSimpleName(), getId(), e.getMessage());
} }
// clear and shut down trade
clearAndShutDown();
// unregister trade // unregister trade
processModel.getTradeManager().unregisterTrade(this); processModel.getTradeManager().unregisterTrade(this);
} }
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Model implementation // Model implementation
@ -1824,6 +1832,13 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
getProtocol().startTimeout(TradeProtocol.TRADE_STEP_TIMEOUT_SECONDS); getProtocol().startTimeout(TradeProtocol.TRADE_STEP_TIMEOUT_SECONDS);
} }
public void stopProtocolTimeout() {
if (!isInitialized) return;
TradeProtocol protocol = getProtocol();
if (protocol == null) return;
protocol.stopTimeout();
}
public void setState(State state) { public void setState(State state) {
if (isInitialized) { if (isInitialized) {
// We don't want to log at startup the setState calls from all persisted trades // We don't want to log at startup the setState calls from all persisted trades

View file

@ -563,9 +563,14 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
Optional<OpenOffer> openOfferOptional = openOfferManager.getOpenOffer(request.getOfferId()); Optional<OpenOffer> openOfferOptional = openOfferManager.getOpenOffer(request.getOfferId());
if (!openOfferOptional.isPresent()) return; if (!openOfferOptional.isPresent()) return;
OpenOffer openOffer = openOfferOptional.get(); OpenOffer openOffer = openOfferOptional.get();
if (openOffer.getState() != OpenOffer.State.AVAILABLE) return;
Offer offer = openOffer.getOffer(); Offer offer = openOffer.getOffer();
// check availability
if (openOffer.getState() != OpenOffer.State.AVAILABLE) {
log.warn("Ignoring InitTradeRequest to maker because offer is not available, offerId={}, sender={}", request.getOfferId(), sender);
return;
}
// validate challenge // validate challenge
if (openOffer.getChallenge() != null && !HavenoUtils.getChallengeHash(openOffer.getChallenge()).equals(HavenoUtils.getChallengeHash(request.getChallenge()))) { if (openOffer.getChallenge() != null && !HavenoUtils.getChallengeHash(openOffer.getChallenge()).equals(HavenoUtils.getChallengeHash(request.getChallenge()))) {
log.warn("Ignoring InitTradeRequest to maker because challenge is incorrect, tradeId={}, sender={}", request.getOfferId(), sender); log.warn("Ignoring InitTradeRequest to maker because challenge is incorrect, tradeId={}, sender={}", request.getOfferId(), sender);
@ -980,9 +985,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
closedTradableManager.add(trade); closedTradableManager.add(trade);
trade.setCompleted(true); trade.setCompleted(true);
removeTrade(trade, true); removeTrade(trade, true);
xmrWalletService.swapPayoutAddressEntryToAvailable(trade.getId()); // TODO The address entry should have been removed already. Check and if its the case remove that.
// TODO The address entry should have been removed already. Check and if its the case remove that.
xmrWalletService.swapPayoutAddressEntryToAvailable(trade.getId());
requestPersistence(); requestPersistence();
} }
@ -990,6 +993,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
log.warn("Unregistering {} {}", trade.getClass().getSimpleName(), trade.getId()); log.warn("Unregistering {} {}", trade.getClass().getSimpleName(), trade.getId());
removeTrade(trade, true); removeTrade(trade, true);
removeFailedTrade(trade); removeFailedTrade(trade);
xmrWalletService.swapPayoutAddressEntryToAvailable(trade.getId()); // TODO The address entry should have been removed already. Check and if its the case remove that.
requestPersistence(); requestPersistence();
} }
@ -1274,11 +1278,15 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
return offer.getDirection() == OfferDirection.SELL; return offer.getDirection() == OfferDirection.SELL;
} }
// TODO (woodser): make Optional<Trade> versus Trade return types consistent // TODO: make Optional<Trade> versus Trade return types consistent
public Trade getTrade(String tradeId) { public Trade getTrade(String tradeId) {
return getOpenTrade(tradeId).orElseGet(() -> getClosedTrade(tradeId).orElseGet(() -> getFailedTrade(tradeId).orElseGet(() -> null))); return getOpenTrade(tradeId).orElseGet(() -> getClosedTrade(tradeId).orElseGet(() -> getFailedTrade(tradeId).orElseGet(() -> null)));
} }
public boolean hasTrade(String tradeId) {
return getTrade(tradeId) != null;
}
public Optional<Trade> getOpenTrade(String tradeId) { public Optional<Trade> getOpenTrade(String tradeId) {
synchronized (tradableList.getList()) { synchronized (tradableList.getList()) {
return tradableList.stream().filter(e -> e.getId().equals(tradeId)).findFirst(); return tradableList.stream().filter(e -> e.getId().equals(tradeId)).findFirst();

View file

@ -842,7 +842,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
} }
} }
protected synchronized void stopTimeout() { public synchronized void stopTimeout() {
synchronized (timeoutTimerLock) { synchronized (timeoutTimerLock) {
if (timeoutTimer != null) { if (timeoutTimer != null) {
timeoutTimer.stop(); timeoutTimer.stop();

View file

@ -346,8 +346,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
List<XmrAddressEntry> addressEntries = xmrWalletService.getAddressEntries(); List<XmrAddressEntry> addressEntries = xmrWalletService.getAddressEntries();
List<DepositListItem> items = new ArrayList<>(); List<DepositListItem> items = new ArrayList<>();
for (XmrAddressEntry addressEntry : addressEntries) { for (XmrAddressEntry addressEntry : addressEntries) {
DepositListItem item = new DepositListItem(addressEntry, xmrWalletService, formatter); if (addressEntry.isTradePayout()) continue; // do not show trade payout addresses
if (addressEntry.isTradePayout() && BigInteger.ZERO.equals(item.getBalanceAsBI())) continue; // do not show empty trade payout addresses
items.add(new DepositListItem(addressEntry, xmrWalletService, formatter)); items.add(new DepositListItem(addressEntry, xmrWalletService, formatter));
} }