Compare commits
7 commits
master
...
corrupt_wa
Author | SHA1 | Date | |
---|---|---|---|
|
475684ab21 | ||
|
03001cab4d | ||
|
9e8d4a8431 | ||
|
6c1fa1770c | ||
|
ff76e39c03 | ||
|
d85a6307f3 | ||
|
64c76291a5 |
10 changed files with 131 additions and 52 deletions
|
@ -74,7 +74,7 @@ public class FileUtil {
|
|||
}
|
||||
}
|
||||
|
||||
public static File getLatestBackupFile(File dir, String fileName) {
|
||||
public static List<File> getBackupFiles(File dir, String fileName) {
|
||||
File backupDir = new File(Paths.get(dir.getAbsolutePath(), BACKUP_DIR).toString());
|
||||
if (!backupDir.exists()) return null;
|
||||
String dirName = "backups_" + fileName;
|
||||
|
@ -82,9 +82,14 @@ public class FileUtil {
|
|||
File backupFileDir = new File(Paths.get(backupDir.getAbsolutePath(), dirName).toString());
|
||||
if (!backupFileDir.exists()) return null;
|
||||
File[] files = backupFileDir.listFiles();
|
||||
if (files == null || files.length == 0) return null;
|
||||
Arrays.sort(files, Comparator.comparing(File::getName));
|
||||
return files[files.length - 1];
|
||||
return Arrays.asList(files);
|
||||
}
|
||||
|
||||
public static File getLatestBackupFile(File dir, String fileName) {
|
||||
List<File> files = getBackupFiles(dir, fileName);
|
||||
if (files.isEmpty()) return null;
|
||||
files.sort(Comparator.comparing(File::getName));
|
||||
return files.get(files.size() - 1);
|
||||
}
|
||||
|
||||
public static void deleteRollingBackup(File dir, String fileName) {
|
||||
|
|
|
@ -466,6 +466,10 @@ public final class Dispute implements NetworkPayload, PersistablePayload {
|
|||
return this.disputeState == State.NEW;
|
||||
}
|
||||
|
||||
public boolean isOpen() {
|
||||
return this.disputeState == State.OPEN || this.disputeState == State.REOPENED;
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return this.disputeState == State.CLOSED;
|
||||
}
|
||||
|
|
|
@ -393,8 +393,14 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
|||
chatMessage.setSystemMessage(true);
|
||||
dispute.addAndPersistChatMessage(chatMessage);
|
||||
|
||||
// export latest multisig hex
|
||||
try {
|
||||
trade.exportMultisigHex();
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to export multisig hex", e);
|
||||
}
|
||||
|
||||
// create dispute opened message
|
||||
trade.exportMultisigHex();
|
||||
NodeAddress agentNodeAddress = getAgentNodeAddress(dispute);
|
||||
DisputeOpenedMessage disputeOpenedMessage = new DisputeOpenedMessage(dispute,
|
||||
p2PService.getAddress(),
|
||||
|
|
|
@ -70,7 +70,7 @@ public abstract class DisputeSession extends SupportSession {
|
|||
|
||||
@Override
|
||||
public boolean chatIsOpen() {
|
||||
return dispute != null && !dispute.isClosed();
|
||||
return dispute != null && dispute.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -937,6 +937,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
if (wallet == null) throw new RuntimeException("Trade wallet to close is not open for trade " + getId());
|
||||
stopPolling();
|
||||
xmrWalletService.closeWallet(wallet, true);
|
||||
maybeBackupWallet();
|
||||
wallet = null;
|
||||
pollPeriodMs = null;
|
||||
}
|
||||
|
@ -1545,9 +1546,6 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
forceCloseWallet();
|
||||
}
|
||||
|
||||
// backup trade wallet if applicable
|
||||
maybeBackupWallet();
|
||||
|
||||
// de-initialize
|
||||
if (idlePayoutSyncer != null) {
|
||||
xmrWalletService.removeWalletListener(idlePayoutSyncer);
|
||||
|
|
|
@ -450,6 +450,13 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
|||
return;
|
||||
}
|
||||
|
||||
// skip if marked as failed
|
||||
if (failedTradesManager.getObservableList().contains(trade)) {
|
||||
log.warn("Skipping initialization of failed trade {} {}", trade.getClass().getSimpleName(), trade.getId());
|
||||
tradesToSkip.add(trade);
|
||||
return;
|
||||
}
|
||||
|
||||
// initialize trade
|
||||
initPersistedTrade(trade);
|
||||
|
||||
|
@ -1059,7 +1066,11 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
|||
|
||||
private void addTradeToPendingTrades(Trade trade) {
|
||||
if (!trade.isInitialized()) {
|
||||
initPersistedTrade(trade);
|
||||
try {
|
||||
initPersistedTrade(trade);
|
||||
} catch (Exception e) {
|
||||
log.warn("Error initializing {} {} on move to pending trades", trade.getClass().getSimpleName(), trade.getShortId(), e);
|
||||
}
|
||||
}
|
||||
addTrade(trade);
|
||||
}
|
||||
|
|
|
@ -121,7 +121,7 @@ public class XmrWalletService extends XmrWalletBase {
|
|||
private static final String MONERO_WALLET_NAME = "haveno_XMR";
|
||||
private static final String KEYS_FILE_POSTFIX = ".keys";
|
||||
private static final String ADDRESS_FILE_POSTFIX = ".address.txt";
|
||||
private static final int NUM_MAX_WALLET_BACKUPS = 1;
|
||||
private static final int NUM_MAX_WALLET_BACKUPS = 2;
|
||||
private static final int MAX_SYNC_ATTEMPTS = 3;
|
||||
private static final boolean PRINT_RPC_STACK_TRACE = false;
|
||||
private static final String THREAD_ID = XmrWalletService.class.getSimpleName();
|
||||
|
@ -1477,26 +1477,33 @@ public class XmrWalletService extends XmrWalletBase {
|
|||
try {
|
||||
walletFull = MoneroWalletFull.openWallet(config);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to open full wallet '{}', attempting to use backup cache, error={}", config.getPath(), e.getMessage());
|
||||
log.warn("Failed to open full wallet '{}', attempting to use backup cache files, error={}", config.getPath(), e.getMessage());
|
||||
boolean retrySuccessful = false;
|
||||
try {
|
||||
|
||||
// rename wallet cache to backup
|
||||
String cachePath = walletDir.toString() + File.separator + MONERO_WALLET_NAME;
|
||||
String cachePath = walletDir.toString() + File.separator + getWalletName(config.getPath());
|
||||
File originalCacheFile = new File(cachePath);
|
||||
if (originalCacheFile.exists()) originalCacheFile.renameTo(new File(cachePath + ".backup"));
|
||||
|
||||
// copy latest wallet cache backup to main folder
|
||||
File backupCacheFile = FileUtil.getLatestBackupFile(walletDir, MONERO_WALLET_NAME);
|
||||
if (backupCacheFile != null) FileUtil.copyFile(backupCacheFile, new File(cachePath));
|
||||
// try opening wallet with backup cache files in descending order
|
||||
List<File> backupCacheFiles = FileUtil.getBackupFiles(walletDir, getWalletName(config.getPath()));
|
||||
Collections.reverse(backupCacheFiles);
|
||||
for (File backupCacheFile : backupCacheFiles) {
|
||||
try {
|
||||
FileUtil.copyFile(backupCacheFile, new File(cachePath));
|
||||
walletFull = MoneroWalletFull.openWallet(config);
|
||||
log.warn("Successfully opened full wallet using backup cache");
|
||||
retrySuccessful = true;
|
||||
break;
|
||||
} catch (Exception e2) {
|
||||
|
||||
// retry opening wallet without original cache
|
||||
try {
|
||||
walletFull = MoneroWalletFull.openWallet(config);
|
||||
log.info("Successfully opened full wallet using backup cache");
|
||||
retrySuccessful = true;
|
||||
} catch (Exception e2) {
|
||||
// ignore
|
||||
// delete cache file if failed to open
|
||||
File cacheFile = new File(cachePath);
|
||||
if (cacheFile.exists()) cacheFile.delete();
|
||||
File unportableCacheFile = new File(cachePath + ".unportable");
|
||||
if (unportableCacheFile.exists()) unportableCacheFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
// handle success or failure
|
||||
|
@ -1505,14 +1512,30 @@ public class XmrWalletService extends XmrWalletBase {
|
|||
if (originalCacheBackup.exists()) originalCacheBackup.delete(); // delete original wallet cache backup
|
||||
} else {
|
||||
|
||||
// restore original wallet cache
|
||||
log.warn("Failed to open full wallet using backup cache, restoring original cache");
|
||||
File cacheFile = new File(cachePath);
|
||||
if (cacheFile.exists()) cacheFile.delete();
|
||||
if (originalCacheBackup.exists()) originalCacheBackup.renameTo(new File(cachePath));
|
||||
// retry opening wallet after cache deleted
|
||||
try {
|
||||
log.warn("Failed to open full wallet using backup cache files, retrying with cache deleted");
|
||||
walletFull = MoneroWalletFull.openWallet(config);
|
||||
log.warn("Successfully opened full wallet after cache deleted");
|
||||
retrySuccessful = true;
|
||||
} catch (Exception e2) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// throw exception
|
||||
throw e;
|
||||
// handle success or failure
|
||||
if (retrySuccessful) {
|
||||
if (originalCacheBackup.exists()) originalCacheBackup.delete(); // delete original wallet cache backup
|
||||
} else {
|
||||
|
||||
// restore original wallet cache
|
||||
log.warn("Failed to open full wallet after deleting cache, restoring original cache");
|
||||
File cacheFile = new File(cachePath);
|
||||
if (cacheFile.exists()) cacheFile.delete();
|
||||
if (originalCacheBackup.exists()) originalCacheBackup.renameTo(new File(cachePath));
|
||||
|
||||
// throw original exception
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} catch (Exception e2) {
|
||||
throw e; // throw original exception
|
||||
|
@ -1582,26 +1605,33 @@ public class XmrWalletService extends XmrWalletBase {
|
|||
try {
|
||||
walletRpc.openWallet(config);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to open RPC wallet '{}', attempting to use backup cache, error={}", config.getPath(), e.getMessage());
|
||||
log.warn("Failed to open RPC wallet '{}', attempting to use backup cache files, error={}", config.getPath(), e.getMessage());
|
||||
boolean retrySuccessful = false;
|
||||
try {
|
||||
|
||||
// rename wallet cache to backup
|
||||
String cachePath = walletDir.toString() + File.separator + MONERO_WALLET_NAME;
|
||||
String cachePath = walletDir.toString() + File.separator + config.getPath();
|
||||
File originalCacheFile = new File(cachePath);
|
||||
if (originalCacheFile.exists()) originalCacheFile.renameTo(new File(cachePath + ".backup"));
|
||||
|
||||
// copy latest wallet cache backup to main folder
|
||||
File backupCacheFile = FileUtil.getLatestBackupFile(walletDir, MONERO_WALLET_NAME);
|
||||
if (backupCacheFile != null) FileUtil.copyFile(backupCacheFile, new File(cachePath));
|
||||
// try opening wallet with backup cache files in descending order
|
||||
List<File> backupCacheFiles = FileUtil.getBackupFiles(walletDir, config.getPath());
|
||||
Collections.reverse(backupCacheFiles);
|
||||
for (File backupCacheFile : backupCacheFiles) {
|
||||
try {
|
||||
FileUtil.copyFile(backupCacheFile, new File(cachePath));
|
||||
walletRpc.openWallet(config);
|
||||
log.warn("Successfully opened RPC wallet using backup cache");
|
||||
retrySuccessful = true;
|
||||
break;
|
||||
} catch (Exception e2) {
|
||||
|
||||
// retry opening wallet without original cache
|
||||
try {
|
||||
walletRpc.openWallet(config);
|
||||
log.info("Successfully opened RPC wallet using backup cache");
|
||||
retrySuccessful = true;
|
||||
} catch (Exception e2) {
|
||||
// ignore
|
||||
// delete cache file if failed to open
|
||||
File cacheFile = new File(cachePath);
|
||||
if (cacheFile.exists()) cacheFile.delete();
|
||||
File unportableCacheFile = new File(cachePath + ".unportable");
|
||||
if (unportableCacheFile.exists()) unportableCacheFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
// handle success or failure
|
||||
|
@ -1610,14 +1640,30 @@ public class XmrWalletService extends XmrWalletBase {
|
|||
if (originalCacheBackup.exists()) originalCacheBackup.delete(); // delete original wallet cache backup
|
||||
} else {
|
||||
|
||||
// restore original wallet cache
|
||||
log.warn("Failed to open RPC wallet using backup cache, restoring original cache");
|
||||
File cacheFile = new File(cachePath);
|
||||
if (cacheFile.exists()) cacheFile.delete();
|
||||
if (originalCacheBackup.exists()) originalCacheBackup.renameTo(new File(cachePath));
|
||||
// retry opening wallet after cache deleted
|
||||
try {
|
||||
log.warn("Failed to open RPC wallet using backup cache files, retrying with cache deleted");
|
||||
walletRpc.openWallet(config);
|
||||
log.warn("Successfully opened RPC wallet after cache deleted");
|
||||
retrySuccessful = true;
|
||||
} catch (Exception e2) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// throw exception
|
||||
throw e;
|
||||
// handle success or failure
|
||||
if (retrySuccessful) {
|
||||
if (originalCacheBackup.exists()) originalCacheBackup.delete(); // delete original wallet cache backup
|
||||
} else {
|
||||
|
||||
// restore original wallet cache
|
||||
log.warn("Failed to open RPC wallet after deleting cache, restoring original cache");
|
||||
File cacheFile = new File(cachePath);
|
||||
if (cacheFile.exists()) cacheFile.delete();
|
||||
if (originalCacheBackup.exists()) originalCacheBackup.renameTo(new File(cachePath));
|
||||
|
||||
// throw original exception
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} catch (Exception e2) {
|
||||
throw e; // throw original exception
|
||||
|
|
|
@ -548,10 +548,17 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
sendDisputeOpenedMessage(dispute, disputeManager);
|
||||
tradeManager.requestPersistence();
|
||||
} else if (useArbitration) {
|
||||
// Only if we have completed mediation we allow arbitration
|
||||
disputeManager = arbitrationManager;
|
||||
Dispute dispute = disputesService.createDisputeForTrade(trade, offer, pubKeyRingProvider.get(), isMaker, isSupportTicket);
|
||||
trade.exportMultisigHex();
|
||||
|
||||
// export latest multisig hex
|
||||
try {
|
||||
trade.exportMultisigHex();
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to export multisig hex", e);
|
||||
}
|
||||
|
||||
// send dispute opened message
|
||||
sendDisputeOpenedMessage(dispute, disputeManager);
|
||||
tradeManager.requestPersistence();
|
||||
} else {
|
||||
|
|
|
@ -404,7 +404,7 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
|
|||
}
|
||||
|
||||
if (trade.getTakerDepositTx() == null) {
|
||||
return Res.get("portfolio.pending.failedTrade.missingDepositTx"); // TODO (woodser): use .missingTakerDepositTx, .missingMakerDepositTx
|
||||
return Res.get("portfolio.pending.failedTrade.missingDepositTx"); // TODO (woodser): use .missingTakerDepositTx, .missingMakerDepositTx, update translation to 2-of-3 multisig
|
||||
}
|
||||
|
||||
if (trade.hasErrorMessage()) {
|
||||
|
|
|
@ -1420,6 +1420,8 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> implements
|
|||
}
|
||||
if (dispute.isClosed()) return Res.get("support.closed");
|
||||
switch (trade.getDisputeState()) {
|
||||
case NO_DISPUTE:
|
||||
return Res.get("shared.pending");
|
||||
case DISPUTE_REQUESTED:
|
||||
return Res.get("support.requested");
|
||||
default:
|
||||
|
|
Loading…
Reference in a new issue