diff --git a/core/src/main/java/bisq/core/api/CoreApi.java b/core/src/main/java/bisq/core/api/CoreApi.java index c4ab13a7a8..fb815ab568 100644 --- a/core/src/main/java/bisq/core/api/CoreApi.java +++ b/core/src/main/java/bisq/core/api/CoreApi.java @@ -247,8 +247,8 @@ public class CoreApi { // Monero node /////////////////////////////////////////////////////////////////////////////////////////// - public boolean isMoneroNodeRunning() { - return coreMoneroNodeService.isMoneroNodeRunning(); + public boolean isMoneroNodeOnline() { + return coreMoneroNodeService.isOnline(); } public MoneroNodeSettings getMoneroNodeSettings() { diff --git a/core/src/main/java/bisq/core/api/CoreMoneroConnectionsService.java b/core/src/main/java/bisq/core/api/CoreMoneroConnectionsService.java index 754973def6..5f4f62355e 100644 --- a/core/src/main/java/bisq/core/api/CoreMoneroConnectionsService.java +++ b/core/src/main/java/bisq/core/api/CoreMoneroConnectionsService.java @@ -5,6 +5,7 @@ import bisq.common.config.Config; import bisq.core.btc.model.EncryptedConnectionList; import bisq.core.btc.setup.DownloadListener; import bisq.core.btc.setup.WalletsSetup; +import bisq.core.trade.TradeUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -237,7 +238,7 @@ public final class CoreMoneroConnectionsService { public long getDefaultRefreshPeriodMs() { if (daemon == null) return REFRESH_PERIOD_LOCAL_MS; else { - boolean isLocal = CoreMoneroNodeService.isLocalHost(daemon.getRpcConnection().getUri()); + boolean isLocal = TradeUtils.isLocalHost(daemon.getRpcConnection().getUri()); if (isLocal) { updateDaemonInfo(); if (lastInfo != null && (lastInfo.isBusySyncing() || (lastInfo.getHeightWithoutBootstrap() != null && lastInfo.getHeightWithoutBootstrap() > 0 && lastInfo.getHeightWithoutBootstrap() < lastInfo.getHeight()))) return REFRESH_PERIOD_REMOTE_MS; // refresh slower if syncing or bootstrapped @@ -321,6 +322,11 @@ public final class CoreMoneroConnectionsService { var currentConnectionUri = connectionList.getCurrentConnectionUri(); if (currentConnectionUri.isPresent()) connectionManager.setConnection(currentConnectionUri.get()); + // set monero connection from startup arguments + if (!isInitialized && !"".equals(config.xmrNode)) { + connectionManager.setConnection(new MoneroRpcConnection(config.xmrNode, config.xmrNodeUsername, config.xmrNodePassword).setPriority(1)); + } + // restore configuration connectionManager.setAutoSwitch(connectionList.getAutoSwitch()); long refreshPeriod = connectionList.getRefreshPeriod(); @@ -331,11 +337,6 @@ public final class CoreMoneroConnectionsService { // run once if (!isInitialized) { - // set monero connection from startup arguments - if (!"".equals(config.xmrNode)) { - connectionManager.setConnection(new MoneroRpcConnection(config.xmrNode, config.xmrNodeUsername, config.xmrNodePassword).setPriority(1)); - } - // register connection change listener connectionManager.addListener(this::onConnectionChanged); @@ -358,10 +359,10 @@ public final class CoreMoneroConnectionsService { isInitialized = true; } - // start local node if offline and last connection is local + // if offline and last connection is local, start local node if offline currentConnectionUri.ifPresent(uri -> { try { - if (CoreMoneroNodeService.isLocalHost(uri) && !nodeService.isMoneroNodeRunning()) { + if (!connectionManager.isConnected() && TradeUtils.isLocalHost(uri) && !nodeService.isOnline()) { nodeService.startMoneroNode(); } } catch (Exception e) { @@ -369,8 +370,10 @@ public final class CoreMoneroConnectionsService { } }); - // connect to local node if available - if (nodeService.isMoneroNodeRunning() && (!connectionManager.isConnected() || connectionManager.getAutoSwitch())) { + // prefer to connect to local node unless prevented by configuration + if (("".equals(config.xmrNode) || TradeUtils.isLocalHost(config.xmrNode)) && + (!connectionManager.isConnected() || connectionManager.getAutoSwitch()) && + nodeService.isConnected()) { MoneroRpcConnection connection = connectionManager.getConnectionByUri(nodeService.getDaemon().getRpcConnection().getUri()); if (connection != null) { connection.checkConnection(connectionManager.getTimeout()); diff --git a/core/src/main/java/bisq/core/api/CoreMoneroNodeService.java b/core/src/main/java/bisq/core/api/CoreMoneroNodeService.java index c465f20d2b..732d72e20a 100644 --- a/core/src/main/java/bisq/core/api/CoreMoneroNodeService.java +++ b/core/src/main/java/bisq/core/api/CoreMoneroNodeService.java @@ -16,6 +16,7 @@ */ package bisq.core.api; +import bisq.core.trade.TradeUtils; import bisq.core.user.Preferences; import bisq.core.xmr.MoneroNodeSettings; import bisq.common.config.BaseCurrencyNetwork; @@ -24,8 +25,6 @@ import bisq.common.config.Config; import javax.inject.Inject; import javax.inject.Singleton; -import java.net.URI; - import java.io.File; import java.io.IOException; @@ -34,7 +33,6 @@ import java.util.Arrays; import java.util.List; import lombok.extern.slf4j.Slf4j; -import monero.common.MoneroError; import monero.daemon.MoneroDaemonRpc; /** @@ -44,8 +42,6 @@ import monero.daemon.MoneroDaemonRpc; @Singleton public class CoreMoneroNodeService { - private static final String LOOPBACK_HOST = "127.0.0.1"; // local loopback address to host Monero node - private static final String LOCALHOST = "localhost"; private static final String MONERO_NETWORK_TYPE = Config.baseCurrencyNetwork().getNetwork().toLowerCase(); private static final String MONEROD_PATH = System.getProperty("user.dir") + File.separator + ".localnet" + File.separator + "monerod"; private static final String MONEROD_DATADIR = Config.baseCurrencyNetwork() == BaseCurrencyNetwork.XMR_LOCAL ? System.getProperty("user.dir") + File.separator + ".localnet" + File.separator + Config.baseCurrencyNetwork().toString().toLowerCase() + File.separator + "node1" : null; @@ -72,19 +68,7 @@ public class CoreMoneroNodeService { else if (Config.baseCurrencyNetwork().isTestnet()) rpcPort = 28081; else if (Config.baseCurrencyNetwork().isStagenet()) rpcPort = 38081; else throw new RuntimeException("Base network is not local testnet, stagenet, or mainnet"); - this.daemon = new MoneroDaemonRpc("http://" + LOOPBACK_HOST + ":" + rpcPort); - } - - /** - * Returns whether the given URI is on local host. // TODO: move to utils - */ - public static boolean isLocalHost(String uri) { - try { - String host = new URI(uri).getHost(); - return host.equals(CoreMoneroNodeService.LOOPBACK_HOST) || host.equals(CoreMoneroNodeService.LOCALHOST); - } catch (Exception e) { - throw new RuntimeException(e); - } + this.daemon = new MoneroDaemonRpc("http://" + TradeUtils.LOOPBACK_HOST + ":" + rpcPort); } public void addListener(MoneroNodeServiceListener listener) { @@ -96,23 +80,30 @@ public class CoreMoneroNodeService { } /** - * Returns the client of the local monero node. + * Return the client of the local Monero node. */ public MoneroDaemonRpc getDaemon() { return daemon; } + private boolean checkConnection() { + return daemon.getRpcConnection().checkConnection(5000); + } + /** - * Returns whether a local monero node is running. + * Check if local Monero node is online. */ - public boolean isMoneroNodeRunning() { - //return daemon.isConnected(); // TODO: daemonRpc.isConnected() should use getVersion() instead of getHeight() which throws when unsynced - try { - daemon.getVersion(); - return true; - } catch (MoneroError e) { - return false; - } + public boolean isOnline() { + checkConnection(); + return daemon.getRpcConnection().isOnline(); + } + + /** + * Check if connected to local Monero node. + */ + public boolean isConnected() { + checkConnection(); + return daemon.getRpcConnection().isConnected(); } public MoneroNodeSettings getMoneroNodeSettings() { @@ -120,7 +111,7 @@ public class CoreMoneroNodeService { } /** - * Starts a local monero node from settings. + * Start a local Monero node from settings. */ public void startMoneroNode() throws IOException { var settings = preferences.getMoneroNodeSettings(); @@ -128,11 +119,11 @@ public class CoreMoneroNodeService { } /** - * Starts a local monero node. Throws MoneroError if the node cannot be started. - * Persists the settings to preferences if the node started successfully. + * Start local Monero node. Throws MoneroError if the node cannot be started. + * Persist the settings to preferences if the node started successfully. */ public void startMoneroNode(MoneroNodeSettings settings) throws IOException { - if (isMoneroNodeRunning()) throw new IllegalStateException("Local Monero node already running"); + if (isOnline()) throw new IllegalStateException("Local Monero node already online"); log.info("Starting local Monero node: " + settings); @@ -160,11 +151,11 @@ public class CoreMoneroNodeService { } /** - * Stops the current local monero node if we own its process. + * Stop the current local Monero node if we own its process. * Does not remove the last MoneroNodeSettings. */ public void stopMoneroNode() { - if (!isMoneroNodeRunning()) throw new IllegalStateException("Local Monero node is not running"); + if (!isOnline()) throw new IllegalStateException("Local Monero node is not running"); if (daemon.getProcess() == null || !daemon.getProcess().isAlive()) throw new IllegalStateException("Cannot stop local Monero node because we don't own its process"); // TODO (woodser): remove isAlive() check after monero-java 0.5.4 which nullifies internal process daemon.stopProcess(); for (var listener : listeners) listener.onNodeStopped(); diff --git a/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java b/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java index 993a445c50..814235dc38 100644 --- a/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java @@ -451,7 +451,7 @@ public class XmrWalletService { try { log.info("Creating wallet " + config.getPath()); walletRpc.createWallet(config); - log.info("Syncing wallet " + config.getPath()); + log.info("Syncing wallet " + config.getPath() + " in background"); walletRpc.startSyncing(connectionsService.getDefaultRefreshPeriodMs()); return walletRpc; } catch (Exception e) { @@ -473,10 +473,13 @@ public class XmrWalletService { walletRpc.openWallet(config); // start syncing wallet in background + log.info("Syncing wallet " + config.getPath() + " in background"); walletRpc.startSyncing(connectionsService.getDefaultRefreshPeriodMs()); // sync wallet (blocks) - walletRpc.sync(); + log.info("Syncing wallet " + config.getPath()); + walletRpc.sync(); // TODO: does this initiate 2 syncs back-to-back? + log.info("Done syncing wallet " + config.getPath()); return walletRpc; } catch (Exception e) { e.printStackTrace(); diff --git a/core/src/main/java/bisq/core/trade/TradeUtils.java b/core/src/main/java/bisq/core/trade/TradeUtils.java index 776f948ddb..887e0fd919 100644 --- a/core/src/main/java/bisq/core/trade/TradeUtils.java +++ b/core/src/main/java/bisq/core/trade/TradeUtils.java @@ -27,6 +27,7 @@ import bisq.core.offer.OfferPayload; import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator; import bisq.core.trade.messages.InitTradeRequest; import bisq.core.util.JsonUtil; +import java.net.URI; import java.util.Objects; import java.util.concurrent.CountDownLatch; @@ -34,7 +35,10 @@ import java.util.concurrent.CountDownLatch; * Collection of utilities for trading. */ public class TradeUtils { - + + public static final String LOOPBACK_HOST = "127.0.0.1"; // local loopback address to host Monero node + public static final String LOCALHOST = "localhost"; + /** * Get address to collect trade fees. * @@ -54,7 +58,19 @@ public class TradeUtils { throw new RuntimeException("Unhandled base currency network: " + Config.baseCurrencyNetwork()); } } - + + /** + * Check if the given URI is on local host. + */ + public static boolean isLocalHost(String uri) { + try { + String host = new URI(uri).getHost(); + return host.equals(LOOPBACK_HOST) || host.equals(LOCALHOST); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + /** * Check if the arbitrator signature for an offer is valid. * diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcMoneroNodeService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcMoneroNodeService.java index a393e19a67..4675e66192 100644 --- a/daemon/src/main/java/bisq/daemon/grpc/GrpcMoneroNodeService.java +++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcMoneroNodeService.java @@ -21,8 +21,8 @@ import bisq.core.xmr.MoneroNodeSettings; import bisq.proto.grpc.GetMoneroNodeSettingsReply; import bisq.proto.grpc.GetMoneroNodeSettingsRequest; -import bisq.proto.grpc.IsMoneroNodeRunningReply; -import bisq.proto.grpc.IsMoneroNodeRunningRequest; +import bisq.proto.grpc.IsMoneroNodeOnlineReply; +import bisq.proto.grpc.IsMoneroNodeOnlineRequest; import bisq.proto.grpc.MoneroNodeGrpc.MoneroNodeImplBase; import bisq.proto.grpc.StartMoneroNodeReply; import bisq.proto.grpc.StartMoneroNodeRequest; @@ -42,7 +42,7 @@ import lombok.extern.slf4j.Slf4j; import static bisq.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor; import static bisq.proto.grpc.MoneroNodeGrpc.getStartMoneroNodeMethod; import static bisq.proto.grpc.MoneroNodeGrpc.getStopMoneroNodeMethod; -import static bisq.proto.grpc.MoneroNodeGrpc.getIsMoneroNodeRunningMethod; +import static bisq.proto.grpc.MoneroNodeGrpc.getIsMoneroNodeOnlineMethod; import static bisq.proto.grpc.MoneroNodeGrpc.getGetMoneroNodeSettingsMethod; import static java.util.concurrent.TimeUnit.SECONDS; @@ -63,11 +63,11 @@ public class GrpcMoneroNodeService extends MoneroNodeImplBase { } @Override - public void isMoneroNodeRunning(IsMoneroNodeRunningRequest request, - StreamObserver responseObserver) { + public void isMoneroNodeOnline(IsMoneroNodeOnlineRequest request, + StreamObserver responseObserver) { try { - var reply = IsMoneroNodeRunningReply.newBuilder() - .setIsRunning(coreApi.isMoneroNodeRunning()) + var reply = IsMoneroNodeOnlineReply.newBuilder() + .setIsRunning(coreApi.isMoneroNodeOnline()) .build(); responseObserver.onNext(reply); responseObserver.onCompleted(); @@ -146,7 +146,7 @@ public class GrpcMoneroNodeService extends MoneroNodeImplBase { .or(() -> Optional.of(CallRateMeteringInterceptor.valueOf( new HashMap<>() {{ int allowedCallsPerTimeWindow = 10; - put(getIsMoneroNodeRunningMethod().getFullMethodName(), new GrpcCallRateMeter(allowedCallsPerTimeWindow, SECONDS)); + put(getIsMoneroNodeOnlineMethod().getFullMethodName(), new GrpcCallRateMeter(allowedCallsPerTimeWindow, SECONDS)); put(getGetMoneroNodeSettingsMethod().getFullMethodName(), new GrpcCallRateMeter(allowedCallsPerTimeWindow, SECONDS)); put(getStartMoneroNodeMethod().getFullMethodName(), new GrpcCallRateMeter(allowedCallsPerTimeWindow, SECONDS)); put(getStopMoneroNodeMethod().getFullMethodName(), new GrpcCallRateMeter(allowedCallsPerTimeWindow, SECONDS)); diff --git a/proto/src/main/proto/grpc.proto b/proto/src/main/proto/grpc.proto index 411e9159d3..268c67235f 100644 --- a/proto/src/main/proto/grpc.proto +++ b/proto/src/main/proto/grpc.proto @@ -387,7 +387,7 @@ message SetAutoSwitchReply {} /////////////////////////////////////////////////////////////////////////////////////////// service MoneroNode { - rpc IsMoneroNodeRunning (IsMoneroNodeRunningRequest) returns (IsMoneroNodeRunningReply) { + rpc IsMoneroNodeOnline (IsMoneroNodeOnlineRequest) returns (IsMoneroNodeOnlineReply) { } rpc GetMoneroNodeSettings (GetMoneroNodeSettingsRequest) returns (GetMoneroNodeSettingsReply) { } @@ -397,10 +397,10 @@ service MoneroNode { } } -message IsMoneroNodeRunningRequest { +message IsMoneroNodeOnlineRequest { } -message IsMoneroNodeRunningReply { +message IsMoneroNodeOnlineReply { bool is_running = 1; }