From ed87b36a763195f97471af327af4f3f448921da8 Mon Sep 17 00:00:00 2001 From: phytohydra <144396848+phytohydra@users.noreply.github.com> Date: Thu, 26 Dec 2024 08:56:53 +0000 Subject: [PATCH 01/10] Add Haveno version to password dialog --- .../src/main/java/haveno/desktop/app/HavenoAppMain.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/desktop/src/main/java/haveno/desktop/app/HavenoAppMain.java b/desktop/src/main/java/haveno/desktop/app/HavenoAppMain.java index b64c5c0d..0656476a 100644 --- a/desktop/src/main/java/haveno/desktop/app/HavenoAppMain.java +++ b/desktop/src/main/java/haveno/desktop/app/HavenoAppMain.java @@ -29,6 +29,7 @@ import haveno.desktop.setup.DesktopPersistedDataHost; import haveno.desktop.util.ImageUtil; import javafx.application.Application; import javafx.application.Platform; +import javafx.geometry.Pos; import javafx.scene.control.ButtonBar; import javafx.scene.control.ButtonType; import javafx.scene.control.Dialog; @@ -210,9 +211,13 @@ public class HavenoAppMain extends HavenoExecutable { Label errorMessageField = new Label(errorMessage); errorMessageField.setTextFill(Color.color(1, 0, 0)); + // Create the version field + Label versionField = new Label(Version.VERSION); + // Set the dialog content VBox vbox = new VBox(10); - vbox.getChildren().addAll(new ImageView(ImageUtil.getImageByPath("logo_splash.png")), passwordField, errorMessageField); + vbox.getChildren().addAll(new ImageView(ImageUtil.getImageByPath("logo_splash.png")), versionField, passwordField, errorMessageField); + vbox.setAlignment(Pos.TOP_CENTER); getDialogPane().setContent(vbox); // Add OK and Cancel buttons From 018ac61054a9f286de24c1407ab57b96458719dc Mon Sep 17 00:00:00 2001 From: woodser Date: Thu, 26 Dec 2024 09:07:02 -0500 Subject: [PATCH 02/10] show reserved balance for offer funding subaddresses and reset if unused --- .../tasks/MakerReserveOfferFunds.java | 14 ++++++++- .../core/xmr/wallet/XmrWalletService.java | 31 +++++++++++++------ .../main/funds/deposit/DepositListItem.java | 2 +- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/haveno/core/offer/placeoffer/tasks/MakerReserveOfferFunds.java b/core/src/main/java/haveno/core/offer/placeoffer/tasks/MakerReserveOfferFunds.java index 4b44a810..a33b9b83 100644 --- a/core/src/main/java/haveno/core/offer/placeoffer/tasks/MakerReserveOfferFunds.java +++ b/core/src/main/java/haveno/core/offer/placeoffer/tasks/MakerReserveOfferFunds.java @@ -33,6 +33,7 @@ import haveno.core.xmr.model.XmrAddressEntry; import lombok.extern.slf4j.Slf4j; import monero.common.MoneroRpcConnection; import monero.daemon.model.MoneroOutput; +import monero.wallet.model.MoneroOutputWallet; import monero.wallet.model.MoneroTxWallet; @Slf4j @@ -62,7 +63,6 @@ public class MakerReserveOfferFunds extends Task { model.getXmrWalletService().getXmrConnectionService().verifyConnection(); // create reserve tx - MoneroTxWallet reserveTx = null; synchronized (HavenoUtils.xmrWalletService.getWalletLock()) { // reset protocol timeout @@ -79,6 +79,7 @@ public class MakerReserveOfferFunds extends Task { Integer preferredSubaddressIndex = fundingEntry == null ? null : fundingEntry.getSubaddressIndex(); // attempt creating reserve tx + MoneroTxWallet reserveTx = null; try { synchronized (HavenoUtils.getWalletFunctionLock()) { for (int i = 0; i < TradeProtocol.MAX_ATTEMPTS; i++) { @@ -121,6 +122,17 @@ public class MakerReserveOfferFunds extends Task { openOffer.setReserveTxHex(reserveTx.getFullHex()); openOffer.setReserveTxKey(reserveTx.getKey()); offer.getOfferPayload().setReserveTxKeyImages(reservedKeyImages); + + // reset offer funding address entry if unused + List inputs = model.getXmrWalletService().getOutputs(reservedKeyImages); + boolean usesFundingEntry = false; + for (MoneroOutputWallet input : inputs) { + if (input.getAccountIndex() == 0 && input.getSubaddressIndex() == fundingEntry.getSubaddressIndex()) { + usesFundingEntry = true; + break; + } + } + if (!usesFundingEntry) model.getXmrWalletService().swapAddressEntryToAvailable(offer.getId(), XmrAddressEntry.Context.OFFER_FUNDING); } complete(); } catch (Throwable t) { diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java index 79e9248c..ccd8d491 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -578,15 +578,6 @@ public class XmrWalletService extends XmrWalletBase { } } - public BigInteger getOutputsAmount(Collection keyImages) { - BigInteger sum = BigInteger.ZERO; - for (String keyImage : keyImages) { - List outputs = getOutputs(new MoneroOutputQuery().setIsSpent(false).setKeyImage(new MoneroKeyImage(keyImage))); - if (!outputs.isEmpty()) sum = sum.add(outputs.get(0).getAmount()); - } - return sum; - } - private List getSubaddressesWithExactInput(BigInteger amount) { // fetch unspent, unfrozen, unlocked outputs @@ -1125,6 +1116,15 @@ public class XmrWalletService extends XmrWalletBase { return subaddress == null ? BigInteger.ZERO : subaddress.getBalance(); } + public BigInteger getBalanceForSubaddress(int subaddressIndex, boolean includeFrozen) { + return getBalanceForSubaddress(subaddressIndex).add(includeFrozen ? getFrozenBalanceForSubaddress(subaddressIndex) : BigInteger.ZERO); + } + + public BigInteger getFrozenBalanceForSubaddress(int subaddressIndex) { + List outputs = getOutputs(new MoneroOutputQuery().setIsFrozen(true).setIsSpent(false).setAccountIndex(0).setSubaddressIndex(subaddressIndex)); + return outputs.stream().map(output -> output.getAmount()).reduce(BigInteger.ZERO, BigInteger::add); + } + public BigInteger getAvailableBalanceForSubaddress(int subaddressIndex) { MoneroSubaddress subaddress = getSubaddress(subaddressIndex); return subaddress == null ? BigInteger.ZERO : subaddress.getUnlockedBalance(); @@ -1250,6 +1250,19 @@ public class XmrWalletService extends XmrWalletBase { return filteredOutputs; } + public List getOutputs(Collection keyImages) { + List outputs = new ArrayList(); + for (String keyImage : keyImages) { + List outputList = getOutputs(new MoneroOutputQuery().setIsSpent(false).setKeyImage(new MoneroKeyImage(keyImage))); + if (!outputList.isEmpty()) outputs.add(outputList.get(0)); + } + return outputs; + } + + public BigInteger getOutputsAmount(Collection keyImages) { + return getOutputs(keyImages).stream().map(output -> output.getAmount()).reduce(BigInteger.ZERO, BigInteger::add); + } + /////////////////////////////////////////////////////////////////////////////////////////// // Util /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositListItem.java b/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositListItem.java index 180b883d..9e21f19b 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositListItem.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositListItem.java @@ -60,7 +60,7 @@ class DepositListItem { this.xmrWalletService = xmrWalletService; this.addressEntry = addressEntry; - balanceAsBI = xmrWalletService.getBalanceForSubaddress(addressEntry.getSubaddressIndex()); + balanceAsBI = xmrWalletService.getBalanceForSubaddress(addressEntry.getSubaddressIndex(), true); balance.set(HavenoUtils.formatXmr(balanceAsBI)); updateUsage(addressEntry.getSubaddressIndex()); From cccd9cf094fe5bb8406dfa1358246de6de34ac26 Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 23 Dec 2024 08:35:39 -0500 Subject: [PATCH 03/10] fix null wallet on error handling --- .../main/java/haveno/core/trade/Trade.java | 11 ++------ .../haveno/core/xmr/wallet/XmrWalletBase.java | 16 ++++++++++- .../core/xmr/wallet/XmrWalletService.java | 27 +++++++++---------- 3 files changed, 29 insertions(+), 25 deletions(-) diff --git a/core/src/main/java/haveno/core/trade/Trade.java b/core/src/main/java/haveno/core/trade/Trade.java index e88f70bd..c5698a61 100644 --- a/core/src/main/java/haveno/core/trade/Trade.java +++ b/core/src/main/java/haveno/core/trade/Trade.java @@ -852,14 +852,6 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { } } - public boolean requestSwitchToNextBestConnection(MoneroRpcConnection sourceConnection) { - if (xmrConnectionService.requestSwitchToNextBestConnection(sourceConnection)) { - onConnectionChanged(xmrConnectionService.getConnection()); // change connection on same thread - return true; - } - return false; - } - public boolean isIdling() { return this instanceof ArbitratorTrade && isDepositsConfirmed() && walletExists() && pollNormalStartTimeMs == null; // arbitrator idles trade after deposits confirm unless overriden } @@ -2386,7 +2378,8 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { return tradeVolumeProperty; } - private void onConnectionChanged(MoneroRpcConnection connection) { + @Override + protected void onConnectionChanged(MoneroRpcConnection connection) { synchronized (walletLock) { // use current connection diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletBase.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletBase.java index 81eada32..06f2e171 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletBase.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletBase.java @@ -17,6 +17,7 @@ import javafx.beans.property.LongProperty; import javafx.beans.property.SimpleLongProperty; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import monero.common.MoneroRpcConnection; import monero.common.TaskLooper; import monero.daemon.model.MoneroTx; import monero.wallet.MoneroWallet; @@ -24,7 +25,7 @@ import monero.wallet.MoneroWalletFull; import monero.wallet.model.MoneroWalletListener; @Slf4j -public class XmrWalletBase { +public abstract class XmrWalletBase { // constants public static final int SYNC_PROGRESS_TIMEOUT_SECONDS = 120; @@ -137,6 +138,19 @@ public class XmrWalletBase { } } + public boolean requestSwitchToNextBestConnection(MoneroRpcConnection sourceConnection) { + if (xmrConnectionService.requestSwitchToNextBestConnection(sourceConnection)) { + onConnectionChanged(xmrConnectionService.getConnection()); // change connection on same thread + return true; + } + return false; + } + + protected abstract void onConnectionChanged(MoneroRpcConnection connection); + + + // ------------------------------ PRIVATE HELPERS ------------------------- + private void updateSyncProgress(long height, long targetHeight) { resetSyncProgressTimeout(); UserThread.execute(() -> { diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java index ccd8d491..fe085de1 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -1334,7 +1334,7 @@ public class XmrWalletService extends XmrWalletBase { maybeInitMainWallet(sync, MAX_SYNC_ATTEMPTS); } - private void maybeInitMainWallet(boolean sync, int numAttempts) { + private void maybeInitMainWallet(boolean sync, int numSyncAttempts) { ThreadUtils.execute(() -> { try { doMaybeInitMainWallet(sync, MAX_SYNC_ATTEMPTS); @@ -1346,7 +1346,7 @@ public class XmrWalletService extends XmrWalletBase { }, THREAD_ID); } - private void doMaybeInitMainWallet(boolean sync, int numAttempts) { + private void doMaybeInitMainWallet(boolean sync, int numSyncAttempts) { synchronized (walletLock) { if (isShutDownStarted) return; @@ -1374,7 +1374,7 @@ public class XmrWalletService extends XmrWalletBase { // sync main wallet if applicable // TODO: error handling and re-initialization is jenky, refactor - if (sync && numAttempts > 0) { + if (sync && numSyncAttempts > 0) { try { // switch connection if disconnected @@ -1393,7 +1393,7 @@ public class XmrWalletService extends XmrWalletBase { log.warn("Error syncing wallet with progress on startup: " + e.getMessage()); forceCloseMainWallet(); requestSwitchToNextBestConnection(sourceConnection); - maybeInitMainWallet(true, numAttempts - 1); // re-initialize wallet and sync again + maybeInitMainWallet(true, numSyncAttempts - 1); // re-initialize wallet and sync again return; } log.info("Done syncing main wallet in " + (System.currentTimeMillis() - time) + " ms"); @@ -1428,8 +1428,8 @@ public class XmrWalletService extends XmrWalletBase { } catch (Exception e) { if (isClosingWallet || isShutDownStarted || HavenoUtils.havenoSetup.getWalletInitialized().get()) return; // ignore if wallet closing, shut down started, or app already initialized log.warn("Error initially syncing main wallet: {}", e.getMessage()); - if (numAttempts <= 1) { - log.warn("Failed to sync main wallet. Opening app without syncing", numAttempts); + if (numSyncAttempts <= 1) { + log.warn("Failed to sync main wallet. Opening app without syncing", numSyncAttempts); HavenoUtils.havenoSetup.getWalletInitialized().set(true); saveMainWallet(false); @@ -1440,7 +1440,7 @@ public class XmrWalletService extends XmrWalletBase { } else { log.warn("Trying again in {} seconds", xmrConnectionService.getRefreshPeriodMs() / 1000); UserThread.runAfter(() -> { - maybeInitMainWallet(true, numAttempts - 1); + maybeInitMainWallet(true, numSyncAttempts - 1); }, xmrConnectionService.getRefreshPeriodMs() / 1000); } } @@ -1754,7 +1754,8 @@ public class XmrWalletService extends XmrWalletBase { return MONERO_WALLET_RPC_MANAGER.startInstance(cmd); } - private void onConnectionChanged(MoneroRpcConnection connection) { + @Override + protected void onConnectionChanged(MoneroRpcConnection connection) { synchronized (walletLock) { // use current connection @@ -1858,13 +1859,13 @@ public class XmrWalletService extends XmrWalletBase { log.warn("Force restarting main wallet"); if (isClosingWallet) return; forceCloseMainWallet(); - maybeInitMainWallet(true); + doMaybeInitMainWallet(true, MAX_SYNC_ATTEMPTS); } public void handleWalletError(Exception e, MoneroRpcConnection sourceConnection) { if (HavenoUtils.isUnresponsive(e)) forceCloseMainWallet(); // wallet can be stuck a while - if (xmrConnectionService.isConnected()) requestSwitchToNextBestConnection(sourceConnection); - getWallet(); // re-open wallet + requestSwitchToNextBestConnection(sourceConnection); + if (wallet == null) doMaybeInitMainWallet(true, MAX_SYNC_ATTEMPTS); } private void startPolling() { @@ -2033,10 +2034,6 @@ public class XmrWalletService extends XmrWalletBase { return requestSwitchToNextBestConnection(null); } - public boolean requestSwitchToNextBestConnection(MoneroRpcConnection sourceConnection) { - return xmrConnectionService.requestSwitchToNextBestConnection(sourceConnection); - } - private void onNewBlock(long height) { UserThread.execute(() -> { walletHeight.set(height); From fc1388d2f4d4ce3d95080e43085ee8b4d4178685 Mon Sep 17 00:00:00 2001 From: woodser Date: Fri, 27 Dec 2024 11:57:47 -0500 Subject: [PATCH 04/10] fix npe accessing funding address entry from api --- .../placeoffer/tasks/MakerReserveOfferFunds.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/haveno/core/offer/placeoffer/tasks/MakerReserveOfferFunds.java b/core/src/main/java/haveno/core/offer/placeoffer/tasks/MakerReserveOfferFunds.java index a33b9b83..0d9271a4 100644 --- a/core/src/main/java/haveno/core/offer/placeoffer/tasks/MakerReserveOfferFunds.java +++ b/core/src/main/java/haveno/core/offer/placeoffer/tasks/MakerReserveOfferFunds.java @@ -124,15 +124,17 @@ public class MakerReserveOfferFunds extends Task { offer.getOfferPayload().setReserveTxKeyImages(reservedKeyImages); // reset offer funding address entry if unused - List inputs = model.getXmrWalletService().getOutputs(reservedKeyImages); - boolean usesFundingEntry = false; - for (MoneroOutputWallet input : inputs) { - if (input.getAccountIndex() == 0 && input.getSubaddressIndex() == fundingEntry.getSubaddressIndex()) { - usesFundingEntry = true; - break; + if (fundingEntry != null) { + List inputs = model.getXmrWalletService().getOutputs(reservedKeyImages); + boolean usesFundingEntry = false; + for (MoneroOutputWallet input : inputs) { + if (input.getAccountIndex() == 0 && input.getSubaddressIndex() == fundingEntry.getSubaddressIndex()) { + usesFundingEntry = true; + break; + } } + if (!usesFundingEntry) model.getXmrWalletService().swapAddressEntryToAvailable(offer.getId(), XmrAddressEntry.Context.OFFER_FUNDING); } - if (!usesFundingEntry) model.getXmrWalletService().swapAddressEntryToAvailable(offer.getId(), XmrAddressEntry.Context.OFFER_FUNDING); } complete(); } catch (Throwable t) { From 6a798312fef2b8f8e622ad4b92b4caa4de4c354a Mon Sep 17 00:00:00 2001 From: phytohydra <144396848+phytohydra@users.noreply.github.com> Date: Fri, 27 Dec 2024 16:56:44 -0800 Subject: [PATCH 05/10] Add version number to splash screen, update version in pw dialog to have a leading "v" --- desktop/src/main/java/haveno/desktop/app/HavenoAppMain.java | 4 ++-- desktop/src/main/java/haveno/desktop/main/MainView.java | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/desktop/src/main/java/haveno/desktop/app/HavenoAppMain.java b/desktop/src/main/java/haveno/desktop/app/HavenoAppMain.java index 0656476a..99cd7a17 100644 --- a/desktop/src/main/java/haveno/desktop/app/HavenoAppMain.java +++ b/desktop/src/main/java/haveno/desktop/app/HavenoAppMain.java @@ -212,8 +212,8 @@ public class HavenoAppMain extends HavenoExecutable { errorMessageField.setTextFill(Color.color(1, 0, 0)); // Create the version field - Label versionField = new Label(Version.VERSION); - + Label versionField = new Label("v" + Version.VERSION); + // Set the dialog content VBox vbox = new VBox(10); vbox.getChildren().addAll(new ImageView(ImageUtil.getImageByPath("logo_splash.png")), versionField, passwordField, errorMessageField); diff --git a/desktop/src/main/java/haveno/desktop/main/MainView.java b/desktop/src/main/java/haveno/desktop/main/MainView.java index 9bf5f378..7d170c87 100644 --- a/desktop/src/main/java/haveno/desktop/main/MainView.java +++ b/desktop/src/main/java/haveno/desktop/main/MainView.java @@ -20,6 +20,7 @@ package haveno.desktop.main; import com.google.inject.Inject; import com.jfoenix.controls.JFXBadge; import com.jfoenix.controls.JFXComboBox; +import haveno.common.app.Version; import haveno.common.HavenoException; import haveno.common.Timer; import haveno.common.UserThread; @@ -510,6 +511,8 @@ public class MainView extends InitializableView { ImageView logo = new ImageView(); logo.setId(Config.baseCurrencyNetwork() == BaseCurrencyNetwork.XMR_MAINNET ? "image-splash-logo" : "image-splash-testnet-logo"); + Label versionLabel = new Label("v" + Version.VERSION); + // createBitcoinInfoBox xmrSplashInfo = new AutoTooltipLabel(); xmrSplashInfo.textProperty().bind(model.getXmrInfo()); @@ -621,7 +624,7 @@ public class MainView extends InitializableView { splashP2PNetworkBox.setPrefHeight(40); splashP2PNetworkBox.getChildren().addAll(splashP2PNetworkLabel, splashP2PNetworkBusyAnimation, splashP2PNetworkIcon, showTorNetworkSettingsButton); - vBox.getChildren().addAll(logo, blockchainSyncBox, xmrSyncIndicator, splashP2PNetworkBox); + vBox.getChildren().addAll(logo, versionLabel, blockchainSyncBox, xmrSyncIndicator, splashP2PNetworkBox); return vBox; } From 2dc7405f8225195279ea3ddd9a85c14512bcdeb7 Mon Sep 17 00:00:00 2001 From: woodser Date: Sat, 28 Dec 2024 08:31:30 -0500 Subject: [PATCH 06/10] log connection read timeouts at info level --- .../network/p2p/network/Connection.java | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/p2p/src/main/java/haveno/network/p2p/network/Connection.java b/p2p/src/main/java/haveno/network/p2p/network/Connection.java index 8165ecd0..79df1714 100644 --- a/p2p/src/main/java/haveno/network/p2p/network/Connection.java +++ b/p2p/src/main/java/haveno/network/p2p/network/Connection.java @@ -178,6 +178,8 @@ public class Connection implements HasCapabilities, Runnable, MessageListener { private static int numThrottledInvalidRequestReports = 0; private static long lastLoggedWarningTs = 0; private static int numThrottledWarnings = 0; + private static long lastLoggedInfoTs = 0; + private static int numThrottledInfos = 0; /////////////////////////////////////////////////////////////////////////////////////////// // Constructor @@ -676,7 +678,7 @@ public class Connection implements HasCapabilities, Runnable, MessageListener { throttleWarn("SocketException (expected if connection lost). closeConnectionReason=" + closeConnectionReason + "; connection=" + this); } else if (e instanceof SocketTimeoutException || e instanceof TimeoutException) { closeConnectionReason = CloseConnectionReason.SOCKET_TIMEOUT; - throttleWarn("Shut down caused by exception " + e.getMessage() + " on connection=" + this); + throttleInfo("Shut down caused by exception " + e.getMessage() + " on connection=" + this); } else if (e instanceof EOFException) { closeConnectionReason = CloseConnectionReason.TERMINATED; throttleWarn("Shut down caused by exception " + e.getMessage() + " on connection=" + this); @@ -937,8 +939,8 @@ public class Connection implements HasCapabilities, Runnable, MessageListener { } private synchronized void throttleWarn(String msg) { - boolean logWarning = System.currentTimeMillis() - lastLoggedWarningTs > LOG_THROTTLE_INTERVAL_MS; - if (logWarning) { + boolean doLog = System.currentTimeMillis() - lastLoggedWarningTs > LOG_THROTTLE_INTERVAL_MS; + if (doLog) { log.warn(msg); if (numThrottledWarnings > 0) log.warn("{} warnings were throttled since the last log entry", numThrottledWarnings); numThrottledWarnings = 0; @@ -947,4 +949,16 @@ public class Connection implements HasCapabilities, Runnable, MessageListener { numThrottledWarnings++; } } + + private synchronized void throttleInfo(String msg) { + boolean doLog = System.currentTimeMillis() - lastLoggedInfoTs > LOG_THROTTLE_INTERVAL_MS; + if (doLog) { + log.info(msg); + if (numThrottledInfos > 0) log.info("{} info logs were throttled since the last log entry", numThrottledInfos); + numThrottledInfos = 0; + lastLoggedInfoTs = System.currentTimeMillis(); + } else { + numThrottledInfos++; + } + } } From 89007c496e0b3f723ab6dd359baf246d4fab7f41 Mon Sep 17 00:00:00 2001 From: woodser Date: Fri, 27 Dec 2024 14:55:54 -0500 Subject: [PATCH 07/10] fix connected status in network settings for current connection --- .../desktop/main/settings/network/NetworkSettingsView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.java b/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.java index 546a5550..399f0508 100644 --- a/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.java +++ b/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.java @@ -521,7 +521,7 @@ public class NetworkSettingsView extends ActivatableView { if (connectionService.isShutDownStarted()) return; // ignore if shutting down moneroNetworkListItems.clear(); moneroNetworkListItems.setAll(connectionService.getConnections().stream() - .map(connection -> new MoneroNetworkListItem(connection, Boolean.TRUE.equals(connection.isConnected()) && connection == connectionService.getConnection())) + .map(connection -> new MoneroNetworkListItem(connection, connection == connectionService.getConnection() && Boolean.TRUE.equals(connectionService.isConnected()))) .collect(Collectors.toList())); updateChainHeightTextField(connectionService.chainHeightProperty().get()); }); From c1b17cf61257a492da3137d9f23e496dbea729bc Mon Sep 17 00:00:00 2001 From: woodser Date: Fri, 27 Dec 2024 14:56:32 -0500 Subject: [PATCH 08/10] update p2p table on user thread to fix null scene --- .../main/settings/network/NetworkSettingsView.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.java b/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.java index 399f0508..0773217c 100644 --- a/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.java +++ b/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.java @@ -508,12 +508,14 @@ public class NetworkSettingsView extends ActivatableView { } private void updateP2PTable() { - if (connectionService.isShutDownStarted()) return; // ignore if shutting down - p2pPeersTableView.getItems().forEach(P2pNetworkListItem::cleanup); - p2pNetworkListItems.clear(); - p2pNetworkListItems.setAll(p2PService.getNetworkNode().getAllConnections().stream() - .map(connection -> new P2pNetworkListItem(connection, clockWatcher)) - .collect(Collectors.toList())); + UserThread.execute(() -> { + if (connectionService.isShutDownStarted()) return; // ignore if shutting down + p2pPeersTableView.getItems().forEach(P2pNetworkListItem::cleanup); + p2pNetworkListItems.clear(); + p2pNetworkListItems.setAll(p2PService.getNetworkNode().getAllConnections().stream() + .map(connection -> new P2pNetworkListItem(connection, clockWatcher)) + .collect(Collectors.toList())); + }); } private void updateMoneroConnectionsTable() { From 0462ddc2731a7e64aa9cd1285d8161975d601322 Mon Sep 17 00:00:00 2001 From: woodser Date: Fri, 27 Dec 2024 08:09:54 -0500 Subject: [PATCH 09/10] backup wallet files before saving --- .../main/java/haveno/core/xmr/wallet/XmrWalletService.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java index fe085de1..89b744cc 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -376,8 +376,8 @@ public class XmrWalletService extends XmrWalletBase { } public void saveWallet(MoneroWallet wallet, boolean backup) { - wallet.save(); if (backup) backupWallet(getWalletName(wallet.getPath())); + wallet.save(); } public void closeWallet(MoneroWallet wallet, boolean save) { @@ -385,8 +385,8 @@ public class XmrWalletService extends XmrWalletBase { MoneroError err = null; String path = wallet.getPath(); try { - wallet.close(save); - if (save) backupWallet(getWalletName(path)); + if (save) saveWallet(wallet, true); + wallet.close(); } catch (MoneroError e) { err = e; } From 9e95de2d7e808052c68ad0a8a6be2fe80e644c2b Mon Sep 17 00:00:00 2001 From: woodser Date: Fri, 27 Dec 2024 10:17:18 -0500 Subject: [PATCH 10/10] save and backup wallet files once per 5 minutes on polling --- .../main/java/haveno/core/trade/Trade.java | 4 ++- .../haveno/core/xmr/wallet/XmrWalletBase.java | 19 ++++++++++- .../core/xmr/wallet/XmrWalletService.java | 32 +++++++++++-------- 3 files changed, 39 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/haveno/core/trade/Trade.java b/core/src/main/java/haveno/core/trade/Trade.java index c5698a61..069b257d 100644 --- a/core/src/main/java/haveno/core/trade/Trade.java +++ b/core/src/main/java/haveno/core/trade/Trade.java @@ -901,6 +901,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { } } + @Override public void requestSaveWallet() { // save wallet off main thread @@ -911,6 +912,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { }, getId()); } + @Override public void saveWallet() { synchronized (walletLock) { if (!walletExists()) { @@ -2675,7 +2677,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { pollInProgress = false; } } - requestSaveWallet(); + saveWalletWithDelay(); } } diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletBase.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletBase.java index 06f2e171..3f4ba3aa 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletBase.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletBase.java @@ -30,11 +30,13 @@ public abstract class XmrWalletBase { // constants public static final int SYNC_PROGRESS_TIMEOUT_SECONDS = 120; public static final int DIRECT_SYNC_WITHIN_BLOCKS = 100; + public static final int SAVE_WALLET_DELAY_SECONDS = 300; // inherited protected MoneroWallet wallet; @Getter protected final Object walletLock = new Object(); + protected Timer saveWalletDelayTimer; @Getter protected XmrConnectionService xmrConnectionService; protected boolean wasWalletSynced; @@ -146,8 +148,23 @@ 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); + } + } + + // --------------------------------- ABSTRACT ----------------------------- + + public abstract void saveWallet(); + + public abstract void requestSaveWallet(); + protected abstract void onConnectionChanged(MoneroRpcConnection connection); - // ------------------------------ PRIVATE HELPERS ------------------------- diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java index 89b744cc..3bf8cfde 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -245,16 +245,20 @@ public class XmrWalletService extends XmrWalletBase { return user.getWalletCreationDate(); } - public void saveMainWallet() { - saveMainWallet(!(Utilities.isWindows() && wallet != null)); + @Override + public void saveWallet() { + saveWallet(!(Utilities.isWindows() && wallet != null)); } - public void saveMainWallet(boolean backup) { - saveWallet(getWallet(), backup); + public void saveWallet(boolean backup) { + synchronized (walletLock) { + saveWallet(getWallet(), backup); + } } - public void requestSaveMainWallet() { - ThreadUtils.submitToPool(() -> saveMainWallet()); // save wallet off main thread + @Override + public void requestSaveWallet() { + ThreadUtils.submitToPool(() -> saveWallet()); // save wallet off main thread } public boolean isWalletAvailable() { @@ -443,7 +447,7 @@ public class XmrWalletService extends XmrWalletBase { if (Boolean.TRUE.equals(txConfig.getRelay())) { cachedTxs.addFirst(tx); cacheWalletInfo(); - requestSaveMainWallet(); + requestSaveWallet(); } return tx; } @@ -453,7 +457,7 @@ public class XmrWalletService extends XmrWalletBase { public String relayTx(String metadata) { synchronized (walletLock) { String txId = wallet.relayTx(metadata); - requestSaveMainWallet(); + requestSaveWallet(); return txId; } } @@ -552,7 +556,7 @@ public class XmrWalletService extends XmrWalletBase { // freeze outputs for (String keyImage : unfrozenKeyImages) wallet.freezeOutput(keyImage); cacheWalletInfo(); - requestSaveMainWallet(); + requestSaveWallet(); } } @@ -574,7 +578,7 @@ public class XmrWalletService extends XmrWalletBase { // thaw outputs for (String keyImage : frozenKeyImages) wallet.thawOutput(keyImage); cacheWalletInfo(); - requestSaveMainWallet(); + requestSaveWallet(); } } @@ -1424,14 +1428,14 @@ public class XmrWalletService extends XmrWalletBase { HavenoUtils.havenoSetup.getWalletInitialized().set(true); // save but skip backup on initialization - saveMainWallet(false); + saveWallet(false); } catch (Exception e) { if (isClosingWallet || isShutDownStarted || HavenoUtils.havenoSetup.getWalletInitialized().get()) return; // ignore if wallet closing, shut down started, or app already initialized log.warn("Error initially syncing main wallet: {}", e.getMessage()); if (numSyncAttempts <= 1) { log.warn("Failed to sync main wallet. Opening app without syncing", numSyncAttempts); HavenoUtils.havenoSetup.getWalletInitialized().set(true); - saveMainWallet(false); + saveWallet(false); // reschedule to init main wallet UserThread.runAfter(() -> { @@ -1809,7 +1813,7 @@ public class XmrWalletService extends XmrWalletBase { tasks.add(() -> { try { wallet.changePassword(oldPassword, newPassword); - saveMainWallet(); + saveWallet(); } catch (Exception e) { log.warn("Error changing main wallet password: " + e.getMessage() + "\n", e); throw e; @@ -2002,7 +2006,7 @@ public class XmrWalletService extends XmrWalletBase { if (wallet != null && !isShutDownStarted) { try { cacheWalletInfo(); - requestSaveMainWallet(); + saveWalletWithDelay(); } catch (Exception e) { log.warn("Error caching wallet info: " + e.getMessage() + "\n", e); }