mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-10-16 04:20:42 -04:00
save wallets after elapsed time and on wallet operations
This commit is contained in:
parent
fb361730c7
commit
37d1b16893
4 changed files with 63 additions and 51 deletions
|
@ -167,7 +167,8 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
private Subscription protocolErrorStateSubscription;
|
||||
private Subscription protocolErrorHeightSubscription;
|
||||
public static final String PROTOCOL_VERSION = "protocolVersion"; // key for extraDataMap in trade statistics
|
||||
public BooleanProperty wasWalletPolled = new SimpleBooleanProperty(false);
|
||||
public BooleanProperty wasWalletPolledProperty = new SimpleBooleanProperty(false);
|
||||
public BooleanProperty wasWalletSyncedAndPolledProperty = new SimpleBooleanProperty(false);
|
||||
private static final long MISSING_TXS_DELAY_MS = Config.baseCurrencyNetwork().isTestnet() ? 5000 : 30000;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -838,16 +839,16 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
getProtocol().maybeReprocessPaymentReceivedMessage(false);
|
||||
HavenoUtils.arbitrationManager.maybeReprocessDisputeClosedMessage(this, false);
|
||||
|
||||
// handle when wallet first polled
|
||||
if (wasWalletPolled.get()) onWalletFirstPolled();
|
||||
// handle when wallet first synced
|
||||
if (wasWalletSyncedAndPolledProperty.get()) onWalletFirstSynced();
|
||||
else {
|
||||
wasWalletPolled.addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue) onWalletFirstPolled();
|
||||
wasWalletSyncedAndPolledProperty.addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue) onWalletFirstSynced();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void onWalletFirstPolled() {
|
||||
private void onWalletFirstSynced() {
|
||||
requestSaveWallet();
|
||||
checkForUnconfirmedTimeout();
|
||||
}
|
||||
|
@ -1000,14 +1001,20 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void requestSaveWallet() {
|
||||
public void requestSaveWalletIfElapsedTime() {
|
||||
ThreadUtils.submitToPool(() -> {
|
||||
synchronized (walletLock) {
|
||||
if (walletExists()) saveWalletIfElapsedTime();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// save wallet off main thread
|
||||
ThreadUtils.execute(() -> {
|
||||
private void requestSaveWallet() {
|
||||
ThreadUtils.submitToPool(() -> {
|
||||
synchronized (walletLock) {
|
||||
if (walletExists()) saveWallet();
|
||||
}
|
||||
}, getId());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1019,6 +1026,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
}
|
||||
if (wallet == null) throw new RuntimeException("Trade wallet is not open for trade " + getShortId());
|
||||
xmrWalletService.saveWallet(wallet);
|
||||
lastSaveTimeMs = System.currentTimeMillis();
|
||||
maybeBackupWallet();
|
||||
}
|
||||
}
|
||||
|
@ -1448,8 +1456,8 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e;
|
||||
HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying
|
||||
} finally {
|
||||
requestSaveWallet();
|
||||
requestPersistence();
|
||||
saveWallet();
|
||||
persistNow(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2394,7 +2402,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
}
|
||||
|
||||
public boolean isDepositTxMissing() {
|
||||
if (!wasWalletPolled.get()) throw new IllegalStateException("Cannot determine if deposit tx is missing because wallet has not been polled");
|
||||
if (!wasWalletPolledProperty.get()) throw new IllegalStateException("Cannot determine if deposit tx is missing because wallet has not been polled");
|
||||
MoneroTxWallet makerDepositTx = getMakerDepositTx();
|
||||
MoneroTxWallet takerDepositTx = getTakerDepositTx();
|
||||
boolean hasUnlockedDepositTx = (makerDepositTx != null && Boolean.FALSE.equals(makerDepositTx.isLocked())) || (takerDepositTx != null && Boolean.FALSE.equals(takerDepositTx.isLocked()));
|
||||
|
@ -2960,10 +2968,12 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
setPayoutTx(txs, checkPool);
|
||||
}
|
||||
|
||||
// update trade period if applicable
|
||||
// update trade period and poll properties
|
||||
maybeUpdateTradePeriod();
|
||||
wasWalletPolledProperty.set(true);
|
||||
if (!offlinePoll) wasWalletSyncedAndPolledProperty.set(true);
|
||||
} catch (Exception e) {
|
||||
if (!(e instanceof IllegalStateException) && !isShutDownStarted && !wasWalletPolled.get()) { // request connection switch if failure on first poll
|
||||
if (!(e instanceof IllegalStateException) && !isShutDownStarted && !offlinePoll && !wasWalletSyncedAndPolledProperty.get()) { // request connection switch on failure until synced and polled
|
||||
ThreadUtils.execute(() -> requestSwitchToNextBestConnection(sourceConnection), getId());
|
||||
}
|
||||
if (HavenoUtils.isUnresponsive(e)) { // wallet can be stuck a while
|
||||
|
@ -2984,8 +2994,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
pollInProgress = false;
|
||||
}
|
||||
}
|
||||
wasWalletPolled.set(true);
|
||||
saveWalletWithDelay();
|
||||
requestSaveWalletIfElapsedTime();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3364,7 +3373,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
if (!skipLog) log.info("Rescanning spent outputs for {} {}", getClass().getSimpleName(), getShortId());
|
||||
wallet.rescanSpent();
|
||||
if (!skipLog) log.info("Done rescanning spent outputs for {} {}", getClass().getSimpleName(), getShortId());
|
||||
saveWalletWithDelay();
|
||||
saveWalletIfElapsedTime();
|
||||
} catch (Exception e) {
|
||||
log.warn("Error rescanning spent outputs for {} {}, errorMessage={}", getClass().getSimpleName(), getShortId(), e.getMessage());
|
||||
if (HavenoUtils.isUnresponsive(e)) forceRestartTradeWallet(); // wallet can be stuck a while
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.concurrent.Future;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import haveno.common.ThreadUtils;
|
||||
import haveno.common.Timer;
|
||||
import haveno.common.UserThread;
|
||||
import haveno.core.api.XmrConnectionService;
|
||||
|
@ -33,9 +34,11 @@ import monero.wallet.model.MoneroWalletListener;
|
|||
public abstract class XmrWalletBase {
|
||||
|
||||
// constants
|
||||
public static final int SYNC_TIMEOUT_SECONDS = 240;
|
||||
public static final int SAVE_WALLET_DELAY_SECONDS = 300;
|
||||
private static final String SYNC_PROGRESS_TIMEOUT_MSG = "Sync progress timeout called";
|
||||
private static final int SYNC_TIMEOUT_SECONDS = 240;
|
||||
private static final String SYNC_TIMEOUT_MSG = "Sync timeout called";
|
||||
private static final long SAVE_AFTER_ELAPSED_SECONDS = 300;
|
||||
private Object saveIntervalLock = new Object();
|
||||
protected long lastSaveTimeMs = 0;
|
||||
|
||||
// inherited
|
||||
protected MoneroWallet wallet;
|
||||
|
@ -80,6 +83,7 @@ public abstract class XmrWalletBase {
|
|||
|
||||
Callable<MoneroSyncResult> task = () -> {
|
||||
MoneroSyncResult result = wallet.sync();
|
||||
saveWalletIfElapsedTime();
|
||||
walletHeight.set(wallet.getHeight());
|
||||
return result;
|
||||
};
|
||||
|
@ -90,7 +94,7 @@ public abstract class XmrWalletBase {
|
|||
return future.get(timeoutSec, TimeUnit.SECONDS);
|
||||
} catch (TimeoutException e) {
|
||||
future.cancel(true);
|
||||
throw new RuntimeException("Sync timed out after " + timeoutSec + " seconds", e);
|
||||
throw new RuntimeException(SYNC_TIMEOUT_MSG, e);
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException("Sync failed", e.getCause());
|
||||
} catch (InterruptedException e) {
|
||||
|
@ -190,7 +194,7 @@ public abstract class XmrWalletBase {
|
|||
if (wallet != null) { // can become null if interrupted by force close
|
||||
if (syncProgressError == null || !HavenoUtils.isUnresponsive(syncProgressError)) { // TODO: skipping stop sync if unresponsive because wallet will hang. if unresponsive, wallet is assumed to be force restarted by caller, but that should be done internally here instead of externally?
|
||||
wallet.stopSyncing();
|
||||
saveWallet();
|
||||
saveWalletIfElapsedTime();
|
||||
}
|
||||
}
|
||||
if (syncProgressError != null) throw new RuntimeException(syncProgressError);
|
||||
|
@ -210,26 +214,27 @@ public abstract class XmrWalletBase {
|
|||
return false;
|
||||
}
|
||||
|
||||
public void saveWalletWithDelay() {
|
||||
// delay writing to disk to avoid frequent write operations
|
||||
if (saveWalletDelayTimer == null) {
|
||||
saveWalletDelayTimer = UserThread.runAfter(() -> {
|
||||
requestSaveWallet();
|
||||
UserThread.execute(() -> saveWalletDelayTimer = null);
|
||||
}, SAVE_WALLET_DELAY_SECONDS, TimeUnit.SECONDS);
|
||||
public void saveWalletIfElapsedTime() {
|
||||
synchronized (saveIntervalLock) {
|
||||
if (System.currentTimeMillis() - lastSaveTimeMs >= SAVE_AFTER_ELAPSED_SECONDS * 1000) {
|
||||
saveWallet();
|
||||
lastSaveTimeMs = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void requestSaveWalletIfElapsedTime() {
|
||||
ThreadUtils.submitToPool(() -> saveWalletIfElapsedTime());
|
||||
}
|
||||
|
||||
public static boolean isSyncWithProgressTimeout(Throwable e) {
|
||||
return e.getMessage().contains(SYNC_TIMEOUT_MSG);
|
||||
}
|
||||
|
||||
// --------------------------------- ABSTRACT -----------------------------
|
||||
|
||||
public static boolean isSyncWithProgressTimeout(Throwable e) {
|
||||
return e.getMessage().contains(SYNC_PROGRESS_TIMEOUT_MSG);
|
||||
}
|
||||
|
||||
public abstract void saveWallet();
|
||||
|
||||
public abstract void requestSaveWallet();
|
||||
|
||||
protected abstract void onConnectionChanged(MoneroRpcConnection connection);
|
||||
|
||||
// ------------------------------ PRIVATE HELPERS -------------------------
|
||||
|
@ -261,7 +266,7 @@ public abstract class XmrWalletBase {
|
|||
if (syncProgressTimeout != null) syncProgressTimeout.stop();
|
||||
syncProgressTimeout = UserThread.runAfter(() -> {
|
||||
if (isShutDownStarted) return;
|
||||
syncProgressError = new RuntimeException(SYNC_PROGRESS_TIMEOUT_MSG);
|
||||
syncProgressError = new RuntimeException(SYNC_TIMEOUT_MSG);
|
||||
syncProgressLatch.countDown();
|
||||
}, SYNC_TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||
}
|
||||
|
|
|
@ -239,7 +239,10 @@ public class XmrWalletService extends XmrWalletBase {
|
|||
|
||||
@Override
|
||||
public void saveWallet() {
|
||||
saveWallet(shouldBackup(wallet));
|
||||
synchronized (walletLock) {
|
||||
saveWallet(shouldBackup(wallet));
|
||||
lastSaveTimeMs = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldBackup(MoneroWallet wallet) {
|
||||
|
@ -252,11 +255,6 @@ public class XmrWalletService extends XmrWalletBase {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestSaveWallet() {
|
||||
ThreadUtils.submitToPool(() -> saveWallet()); // save wallet off main thread
|
||||
}
|
||||
|
||||
public boolean isWalletAvailable() {
|
||||
try {
|
||||
return getWallet() != null;
|
||||
|
@ -432,7 +430,7 @@ public class XmrWalletService extends XmrWalletBase {
|
|||
if (Boolean.TRUE.equals(txConfig.getRelay())) {
|
||||
cachedTxs.addFirst(tx);
|
||||
cacheWalletInfo();
|
||||
requestSaveWallet();
|
||||
saveWallet();
|
||||
}
|
||||
return tx;
|
||||
}
|
||||
|
@ -450,7 +448,7 @@ public class XmrWalletService extends XmrWalletBase {
|
|||
if (Boolean.TRUE.equals(txConfig.getRelay())) {
|
||||
for (MoneroTxWallet tx : txs) cachedTxs.addFirst(tx);
|
||||
cacheWalletInfo();
|
||||
requestSaveWallet();
|
||||
saveWallet();
|
||||
}
|
||||
return txs;
|
||||
}
|
||||
|
@ -460,7 +458,7 @@ public class XmrWalletService extends XmrWalletBase {
|
|||
public List<String> relayTxs(List<String> metadatas) {
|
||||
synchronized (walletLock) {
|
||||
List<String> txIds = wallet.relayTxs(metadatas);
|
||||
requestSaveWallet();
|
||||
saveWallet();
|
||||
return txIds;
|
||||
}
|
||||
}
|
||||
|
@ -554,7 +552,7 @@ public class XmrWalletService extends XmrWalletBase {
|
|||
for (String keyImage : unfrozenKeyImages) wallet.freezeOutput(keyImage);
|
||||
cacheNonPoolTxs();
|
||||
cacheWalletInfo();
|
||||
requestSaveWallet();
|
||||
saveWallet();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -577,7 +575,7 @@ public class XmrWalletService extends XmrWalletBase {
|
|||
for (String keyImage : frozenKeyImages) wallet.thawOutput(keyImage);
|
||||
cacheNonPoolTxs();
|
||||
cacheWalletInfo();
|
||||
requestSaveWallet();
|
||||
saveWallet();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2075,7 +2073,7 @@ public class XmrWalletService extends XmrWalletBase {
|
|||
pollInProgress = false;
|
||||
}
|
||||
}
|
||||
saveWalletWithDelay();
|
||||
saveWalletIfElapsedTime();
|
||||
|
||||
// cache wallet info last
|
||||
synchronized (walletLock) {
|
||||
|
|
|
@ -257,8 +257,8 @@ public abstract class TradeStepView extends AnchorPane {
|
|||
}
|
||||
});
|
||||
|
||||
if (trade.wasWalletPolled.get()) addTradeStateSubscription();
|
||||
else trade.wasWalletPolled.addListener((observable, oldValue, newValue) -> {
|
||||
if (trade.wasWalletSyncedAndPolledProperty.get()) addTradeStateSubscription();
|
||||
else trade.wasWalletSyncedAndPolledProperty.addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue) addTradeStateSubscription();
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue