diff --git a/core/src/main/java/haveno/core/api/XmrConnectionService.java b/core/src/main/java/haveno/core/api/XmrConnectionService.java index 0c78fc9875f..7260715d7d4 100644 --- a/core/src/main/java/haveno/core/api/XmrConnectionService.java +++ b/core/src/main/java/haveno/core/api/XmrConnectionService.java @@ -36,7 +36,10 @@ import haveno.network.p2p.P2PService; import haveno.network.p2p.P2PServiceListener; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import javafx.beans.property.IntegerProperty; import javafx.beans.property.LongProperty; @@ -201,12 +204,24 @@ public List getConnections() { return connectionManager.getConnections(); } - public void switchToBestConnection() { - if (isFixedConnection() || !connectionManager.getAutoSwitch()) return; + private void switchToBestConnection() { + if (isFixedConnection() || !connectionManager.getAutoSwitch()) { + log.info("Skipping switch to best Monero connection because connection is fixed or auto switch is disabled"); + return; + } MoneroRpcConnection bestConnection = getBestAvailableConnection(); if (bestConnection != null) setConnection(bestConnection); } + public void switchToNextBestConnection() { + if (isFixedConnection() || !connectionManager.getAutoSwitch()) { + log.info("Skipping switch to next best Monero connection because connection is fixed or auto switch is disabled"); + return; + } + MoneroRpcConnection bestConnection = getBestAvailableConnection(List.of(getConnection())); + if (bestConnection != null) setConnection(bestConnection); + } + public void setConnection(String connectionUri) { accountService.checkAccountOpen(); connectionManager.setConnection(connectionUri); // listener will update connection list @@ -244,10 +259,21 @@ public void stopCheckingConnection() { public MoneroRpcConnection getBestAvailableConnection() { accountService.checkAccountOpen(); List ignoredConnections = new ArrayList(); - if (xmrLocalNode.shouldBeIgnored() && connectionManager.hasConnection(xmrLocalNode.getUri())) ignoredConnections.add(connectionManager.getConnectionByUri(xmrLocalNode.getUri())); + addLocalNodeIfIgnored(ignoredConnections); return connectionManager.getBestAvailableConnection(ignoredConnections.toArray(new MoneroRpcConnection[0])); } + private MoneroRpcConnection getBestAvailableConnection(Collection ignoredConnections) { + accountService.checkAccountOpen(); + Set ignoredConnectionsSet = new HashSet<>(ignoredConnections); + addLocalNodeIfIgnored(ignoredConnectionsSet); + return connectionManager.getBestAvailableConnection(ignoredConnectionsSet.toArray(new MoneroRpcConnection[0])); + } + + private void addLocalNodeIfIgnored(Collection ignoredConnections) { + if (xmrLocalNode.shouldBeIgnored() && connectionManager.hasConnection(xmrLocalNode.getUri())) ignoredConnections.add(connectionManager.getConnectionByUri(xmrLocalNode.getUri())); + } + public void setAutoSwitch(boolean autoSwitch) { accountService.checkAccountOpen(); connectionManager.setAutoSwitch(autoSwitch); diff --git a/core/src/main/java/haveno/core/trade/Trade.java b/core/src/main/java/haveno/core/trade/Trade.java index 6aa00373070..ae2f5a58ed5 100644 --- a/core/src/main/java/haveno/core/trade/Trade.java +++ b/core/src/main/java/haveno/core/trade/Trade.java @@ -125,6 +125,7 @@ import java.util.Date; import java.util.List; import java.util.Optional; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; @@ -627,9 +628,7 @@ public void initialize(ProcessModelServiceProvider serviceProvider) { // handle connection change on dedicated thread xmrConnectionService.addConnectionListener(connection -> { - ThreadUtils.submitToPool(() -> { // TODO: remove this? - ThreadUtils.execute(() -> onConnectionChanged(connection), getConnectionChangedThreadId()); - }); + ThreadUtils.execute(() -> onConnectionChanged(connection), getConnectionChangedThreadId()); }); // reset buyer's payment sent state if no ack receive @@ -1133,10 +1132,10 @@ private MoneroTxWallet doCreatePayoutTx() { // check if multisig import needed if (wallet.isMultisigImportNeeded()) throw new RuntimeException("Cannot create payout tx because multisig import is needed"); - // TODO: wallet sometimes returns empty data, after disconnect? - List txs = wallet.getTxs(); // TODO: this fetches from pool - if (txs.isEmpty()) { - log.warn("Restarting wallet for {} {} because deposit txs are missing to create payout tx", getClass().getSimpleName(), getId()); + // TODO: wallet sometimes returns empty data, due to unreliable connection with specific daemons? + if (getMakerDepositTx() == null || getTakerDepositTx() == null) { + log.warn("Switching Monero connection and restarting trade wallet because deposit txs are missing to create payout tx for {} {}", getClass().getSimpleName(), getShortId()); + switchToNextBestConnection(); forceRestartTradeWallet(); } @@ -2236,10 +2235,6 @@ private void doPublishTradeStatistics() { // Private /////////////////////////////////////////////////////////////////////////////////////////// - private String getConnectionChangedThreadId() { - return getId() + ".onConnectionChanged"; - } - // lazy initialization private ObjectProperty getAmountProperty() { if (tradeAmountProperty == null) @@ -2255,6 +2250,17 @@ private ObjectProperty getVolumeProperty() { return tradeVolumeProperty; } + private String getConnectionChangedThreadId() { + return getId() + ".onConnectionChanged"; + } + + private void switchToNextBestConnection() { + xmrConnectionService.switchToNextBestConnection(); + CountDownLatch latch = new CountDownLatch(1); + ThreadUtils.execute(() -> latch.countDown(), getConnectionChangedThreadId()); // wait for connection change to complete + HavenoUtils.awaitLatch(latch); + } + private void onConnectionChanged(MoneroRpcConnection connection) { synchronized (walletLock) { 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 682aafdb490..41d93f50049 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -1817,7 +1817,7 @@ private void doPollWallet(boolean updateTxs) { // switch to best connection if wallet is too far behind if (wasWalletSynced && walletHeight.get() < xmrConnectionService.getTargetHeight() - NUM_BLOCKS_BEHIND_TOLERANCE && !Config.baseCurrencyNetwork().isTestnet()) { log.warn("Updating connection because main wallet is {} blocks behind monerod, wallet height={}, monerod height={}", xmrConnectionService.getTargetHeight() - walletHeight.get(), walletHeight.get(), lastInfo.getHeight()); - xmrConnectionService.switchToBestConnection(); + xmrConnectionService.switchToNextBestConnection(); } // sync wallet if behind daemon