diff --git a/core/src/main/java/haveno/core/api/CoreMoneroConnectionsService.java b/core/src/main/java/haveno/core/api/CoreMoneroConnectionsService.java index a98bcf6237..9e3967b470 100644 --- a/core/src/main/java/haveno/core/api/CoreMoneroConnectionsService.java +++ b/core/src/main/java/haveno/core/api/CoreMoneroConnectionsService.java @@ -44,16 +44,13 @@ import java.util.stream.Collectors; public final class CoreMoneroConnectionsService { private static final int MIN_BROADCAST_CONNECTIONS = 0; // TODO: 0 for stagenet, 5+ for mainnet - private static final long REFRESH_PERIOD_LOCAL_MS = 5000; // refresh period when connected to local node private static final long REFRESH_PERIOD_HTTP_MS = 20000; // refresh period when connected to remote node over http private static final long REFRESH_PERIOD_ONION_MS = 30000; // refresh period when connected to remote node over tor private static final long MIN_ERROR_LOG_PERIOD_MS = 300000; // minimum period between logging errors fetching daemon info private static Long lastErrorTimestamp; - - private final Object lock = new Object(); - private final Object listenersLock = new Object(); + private final Object listenerLock = new Object(); private final Config config; private final CoreContext coreContext; private final Preferences preferences; @@ -120,7 +117,7 @@ public final class CoreMoneroConnectionsService { public void onShutDownStarted() { log.info("{}.onShutDownStarted()", getClass().getSimpleName()); isShutDownStarted = true; - synchronized (this) { + synchronized (lock) { // ensures request not in progress } } @@ -147,7 +144,7 @@ public final class CoreMoneroConnectionsService { } public void addConnectionListener(MoneroConnectionManagerListener listener) { - synchronized (listenersLock) { + synchronized (listenerLock) { listeners.add(listener); } } @@ -319,10 +316,10 @@ public final class CoreMoneroConnectionsService { private long getDefaultRefreshPeriodMs() { MoneroRpcConnection connection = getConnection(); - if (connection == null) return REFRESH_PERIOD_LOCAL_MS; + if (connection == null) return LocalMoneroNode.REFRESH_PERIOD_LOCAL_MS; if (isConnectionLocal(connection)) { if (lastInfo != null && (lastInfo.isBusySyncing() || (lastInfo.getHeightWithoutBootstrap() != null && lastInfo.getHeightWithoutBootstrap() > 0 && lastInfo.getHeightWithoutBootstrap() < lastInfo.getHeight()))) return REFRESH_PERIOD_HTTP_MS; // refresh slower if syncing or bootstrapped - else return REFRESH_PERIOD_LOCAL_MS; // TODO: announce faster refresh after done syncing + else return LocalMoneroNode.REFRESH_PERIOD_LOCAL_MS; // TODO: announce faster refresh after done syncing } else if (useProxy(connection)) { return REFRESH_PERIOD_ONION_MS; } else { @@ -375,16 +372,23 @@ public final class CoreMoneroConnectionsService { nodeService.addListener(new LocalMoneroNodeListener() { @Override public void onNodeStarted(MoneroDaemonRpc daemon) { - log.info(getClass() + ".onNodeStarted() called"); - daemon.getRpcConnection().checkConnection(connectionManager.getTimeout()); - setConnection(daemon.getRpcConnection()); - checkConnection(); + log.info("Local monero node started"); } @Override public void onNodeStopped() { - log.info(getClass() + ".onNodeStopped() called"); - checkConnection(); + log.info("Local monero node stopped"); + } + + @Override + public void onConnectionChanged(MoneroRpcConnection connection) { + log.info("Local monerod connection changed: " + connection); + if (isShutDownStarted || !connectionManager.getAutoSwitch() || !accountService.isAccountOpen()) return; + if (nodeService.isConnected()) { + setConnection(connection.getUri()); // switch to local node if connected + } else if (getConnection() != null && getConnection().getUri().equals(connection.getUri())) { + setConnection(getBestAvailableConnection()); // switch to best available if disconnected from local node + } } }); } @@ -514,7 +518,7 @@ public final class CoreMoneroConnectionsService { updatePolling(); // notify listeners in parallel - synchronized (listenersLock) { + synchronized (listenerLock) { for (MoneroConnectionManagerListener listener : listeners) { new Thread(() -> listener.onConnectionChanged(currentConnection)).start(); } diff --git a/core/src/main/java/haveno/core/api/LocalMoneroNode.java b/core/src/main/java/haveno/core/api/LocalMoneroNode.java index 87d679789c..cae0d0d737 100644 --- a/core/src/main/java/haveno/core/api/LocalMoneroNode.java +++ b/core/src/main/java/haveno/core/api/LocalMoneroNode.java @@ -23,6 +23,7 @@ import haveno.core.trade.HavenoUtils; import haveno.core.user.Preferences; import haveno.core.xmr.MoneroNodeSettings; import lombok.extern.slf4j.Slf4j; +import monero.common.MoneroConnectionManager; import monero.common.MoneroUtils; import monero.daemon.MoneroDaemonRpc; import javax.inject.Inject; @@ -39,11 +40,16 @@ import java.util.List; @Singleton public class LocalMoneroNode { + // constants + public static final long REFRESH_PERIOD_LOCAL_MS = 5000; // refresh period for local node public static final String MONEROD_DIR = Config.baseCurrencyNetwork() == BaseCurrencyNetwork.XMR_LOCAL ? System.getProperty("user.dir") + File.separator + ".localnet" : Config.appDataDir().getAbsolutePath(); public static final String MONEROD_NAME = Utilities.isWindows() ? "monerod.exe" : "monerod"; public static final String MONEROD_PATH = MONEROD_DIR + File.separator + MONEROD_NAME; private static final String MONEROD_DATADIR = Config.baseCurrencyNetwork() == BaseCurrencyNetwork.XMR_LOCAL ? MONEROD_DIR + File.separator + Config.baseCurrencyNetwork().toString().toLowerCase() + File.separator + "node1" : null; // use default directory unless local + // instance fields + private MoneroDaemonRpc daemon; + private MoneroConnectionManager connectionManager; private final Config config; private final Preferences preferences; private final List listeners = new ArrayList<>(); @@ -57,8 +63,7 @@ public class LocalMoneroNode { if (!Config.baseCurrencyNetwork().isMainnet()) MONEROD_ARGS.add("--" + Config.baseCurrencyNetwork().getNetwork().toLowerCase()); } - // client to the local Monero node - private MoneroDaemonRpc daemon; + // default rpc ports private static Integer rpcPort; static { if (Config.baseCurrencyNetwork().isMainnet()) rpcPort = 18081; @@ -72,6 +77,15 @@ public class LocalMoneroNode { this.config = config; this.preferences = preferences; this.daemon = new MoneroDaemonRpc("http://" + HavenoUtils.LOOPBACK_HOST + ":" + rpcPort); + + // initialize connection manager to listen to local connection + this.connectionManager = new MoneroConnectionManager().setConnection(daemon.getRpcConnection()); + this.connectionManager.setTimeout(REFRESH_PERIOD_LOCAL_MS); + this.connectionManager.checkConnection(); + this.connectionManager.addListener((connection) -> { + for (var listener : listeners) listener.onConnectionChanged(connection); // notify of connection changes + }); + this.connectionManager.startPolling(REFRESH_PERIOD_LOCAL_MS); } /** @@ -106,10 +120,6 @@ public class LocalMoneroNode { return daemon; } - private boolean checkConnection() { - return daemon.getRpcConnection().checkConnection(5000); - } - public boolean equalsUri(String uri) { return HavenoUtils.isLocalHost(uri) && MoneroUtils.parseUri(uri).getPort() == rpcPort; } @@ -119,7 +129,7 @@ public class LocalMoneroNode { */ public boolean isDetected() { checkConnection(); - return daemon.getRpcConnection().isOnline(); + return Boolean.TRUE.equals(connectionManager.getConnection().isOnline()); } /** @@ -127,7 +137,11 @@ public class LocalMoneroNode { */ public boolean isConnected() { checkConnection(); - return daemon.getRpcConnection().isConnected(); + return Boolean.TRUE.equals(connectionManager.isConnected()); + } + + private void checkConnection() { + connectionManager.checkConnection(); } public MoneroNodeSettings getMoneroNodeSettings() { diff --git a/core/src/main/java/haveno/core/api/LocalMoneroNodeListener.java b/core/src/main/java/haveno/core/api/LocalMoneroNodeListener.java index 844a4598d4..d5c3eeee9f 100644 --- a/core/src/main/java/haveno/core/api/LocalMoneroNodeListener.java +++ b/core/src/main/java/haveno/core/api/LocalMoneroNodeListener.java @@ -16,9 +16,11 @@ */ package haveno.core.api; +import monero.common.MoneroRpcConnection; import monero.daemon.MoneroDaemonRpc; public class LocalMoneroNodeListener { public void onNodeStarted(MoneroDaemonRpc daemon) {} public void onNodeStopped() {} + public void onConnectionChanged(MoneroRpcConnection connection) {} }