mirror of
https://github.com/haveno-dex/haveno.git
synced 2024-12-22 11:39:29 +00:00
prompt to fall back on startup error with custom node
Some checks failed
CI / build (macos-13) (push) Has been cancelled
CI / build (ubuntu-22.04) (push) Has been cancelled
CI / build (windows-latest) (push) Has been cancelled
Codacy Coverage Reporter / Publish coverage (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled
Some checks failed
CI / build (macos-13) (push) Has been cancelled
CI / build (ubuntu-22.04) (push) Has been cancelled
CI / build (windows-latest) (push) Has been cancelled
Codacy Coverage Reporter / Publish coverage (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled
This commit is contained in:
parent
1aef8a6bab
commit
7f6d28f1fb
8 changed files with 89 additions and 20 deletions
|
@ -43,6 +43,7 @@ import java.util.Set;
|
||||||
|
|
||||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
|
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.IntegerProperty;
|
import javafx.beans.property.IntegerProperty;
|
||||||
import javafx.beans.property.LongProperty;
|
import javafx.beans.property.LongProperty;
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
@ -50,6 +51,7 @@ import javafx.beans.property.ReadOnlyDoubleProperty;
|
||||||
import javafx.beans.property.ReadOnlyIntegerProperty;
|
import javafx.beans.property.ReadOnlyIntegerProperty;
|
||||||
import javafx.beans.property.ReadOnlyLongProperty;
|
import javafx.beans.property.ReadOnlyLongProperty;
|
||||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.beans.property.SimpleIntegerProperty;
|
import javafx.beans.property.SimpleIntegerProperty;
|
||||||
import javafx.beans.property.SimpleLongProperty;
|
import javafx.beans.property.SimpleLongProperty;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
@ -89,6 +91,8 @@ public final class XmrConnectionService {
|
||||||
private final LongProperty chainHeight = new SimpleLongProperty(0);
|
private final LongProperty chainHeight = new SimpleLongProperty(0);
|
||||||
private final DownloadListener downloadListener = new DownloadListener();
|
private final DownloadListener downloadListener = new DownloadListener();
|
||||||
@Getter
|
@Getter
|
||||||
|
private final BooleanProperty connectionServiceFallbackHandlerActive = new SimpleBooleanProperty();
|
||||||
|
@Getter
|
||||||
private final StringProperty connectionServiceErrorMsg = new SimpleStringProperty();
|
private final StringProperty connectionServiceErrorMsg = new SimpleStringProperty();
|
||||||
private final LongProperty numUpdates = new SimpleLongProperty(0);
|
private final LongProperty numUpdates = new SimpleLongProperty(0);
|
||||||
private Socks5ProxyProvider socks5ProxyProvider;
|
private Socks5ProxyProvider socks5ProxyProvider;
|
||||||
|
@ -99,6 +103,7 @@ public final class XmrConnectionService {
|
||||||
private Boolean isConnected = false;
|
private Boolean isConnected = false;
|
||||||
@Getter
|
@Getter
|
||||||
private MoneroDaemonInfo lastInfo;
|
private MoneroDaemonInfo lastInfo;
|
||||||
|
private Long lastFallbackInvocation;
|
||||||
private Long lastLogPollErrorTimestamp;
|
private Long lastLogPollErrorTimestamp;
|
||||||
private long lastLogDaemonNotSyncedTimestamp;
|
private long lastLogDaemonNotSyncedTimestamp;
|
||||||
private Long syncStartHeight;
|
private Long syncStartHeight;
|
||||||
|
@ -115,6 +120,8 @@ public final class XmrConnectionService {
|
||||||
private int numRequestsLastMinute;
|
private int numRequestsLastMinute;
|
||||||
private long lastSwitchTimestamp;
|
private long lastSwitchTimestamp;
|
||||||
private Set<MoneroRpcConnection> excludedConnections = new HashSet<>();
|
private Set<MoneroRpcConnection> excludedConnections = new HashSet<>();
|
||||||
|
private static final long FALLBACK_INVOCATION_PERIOD_MS = 1000 * 60 * 1; // offer to fallback up to once every minute
|
||||||
|
private boolean fallbackApplied;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public XmrConnectionService(P2PService p2PService,
|
public XmrConnectionService(P2PService p2PService,
|
||||||
|
@ -424,6 +431,19 @@ public final class XmrConnectionService {
|
||||||
return numUpdates;
|
return numUpdates;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void fallbackToBestConnection() {
|
||||||
|
if (isShutDownStarted) return;
|
||||||
|
if (xmrNodes.getProvidedXmrNodes().isEmpty()) {
|
||||||
|
log.warn("Falling back to public nodes");
|
||||||
|
preferences.setMoneroNodesOptionOrdinal(XmrNodes.MoneroNodesOption.PUBLIC.ordinal());
|
||||||
|
} else {
|
||||||
|
log.warn("Falling back to provided nodes");
|
||||||
|
preferences.setMoneroNodesOptionOrdinal(XmrNodes.MoneroNodesOption.PROVIDED.ordinal());
|
||||||
|
}
|
||||||
|
fallbackApplied = true;
|
||||||
|
initializeConnections();
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------- HELPERS --------------------------------
|
// ------------------------------- HELPERS --------------------------------
|
||||||
|
|
||||||
private void doneDownload() {
|
private void doneDownload() {
|
||||||
|
@ -533,7 +553,7 @@ public final class XmrConnectionService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore connections
|
// restore connections
|
||||||
if ("".equals(config.xmrNode)) {
|
if (!isFixedConnection()) {
|
||||||
|
|
||||||
// load previous or default connections
|
// load previous or default connections
|
||||||
if (coreContext.isApiUser()) {
|
if (coreContext.isApiUser()) {
|
||||||
|
@ -569,10 +589,7 @@ public final class XmrConnectionService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore last connection
|
// restore last connection
|
||||||
if (isFixedConnection()) {
|
if (connectionList.getCurrentConnectionUri().isPresent() && connectionManager.hasConnection(connectionList.getCurrentConnectionUri().get())) {
|
||||||
if (getConnections().size() != 1) throw new IllegalStateException("Expected connection list to have 1 fixed connection but was: " + getConnections().size());
|
|
||||||
connectionManager.setConnection(getConnections().get(0));
|
|
||||||
} else if (connectionList.getCurrentConnectionUri().isPresent() && connectionManager.hasConnection(connectionList.getCurrentConnectionUri().get())) {
|
|
||||||
if (!xmrLocalNode.shouldBeIgnored() || !xmrLocalNode.equalsUri(connectionList.getCurrentConnectionUri().get())) {
|
if (!xmrLocalNode.shouldBeIgnored() || !xmrLocalNode.equalsUri(connectionList.getCurrentConnectionUri().get())) {
|
||||||
connectionManager.setConnection(connectionList.getCurrentConnectionUri().get());
|
connectionManager.setConnection(connectionList.getCurrentConnectionUri().get());
|
||||||
}
|
}
|
||||||
|
@ -592,7 +609,7 @@ public final class XmrConnectionService {
|
||||||
maybeStartLocalNode();
|
maybeStartLocalNode();
|
||||||
|
|
||||||
// update connection
|
// update connection
|
||||||
if (!isFixedConnection() && (connectionManager.getConnection() == null || connectionManager.getAutoSwitch())) {
|
if (connectionManager.getConnection() == null || connectionManager.getAutoSwitch()) {
|
||||||
MoneroRpcConnection bestConnection = getBestAvailableConnection();
|
MoneroRpcConnection bestConnection = getBestAvailableConnection();
|
||||||
if (bestConnection != null) setConnection(bestConnection);
|
if (bestConnection != null) setConnection(bestConnection);
|
||||||
}
|
}
|
||||||
|
@ -614,6 +631,7 @@ public final class XmrConnectionService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// notify initial connection
|
// notify initial connection
|
||||||
|
lastRefreshPeriodMs = getRefreshPeriodMs();
|
||||||
onConnectionChanged(connectionManager.getConnection());
|
onConnectionChanged(connectionManager.getConnection());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -716,16 +734,14 @@ public final class XmrConnectionService {
|
||||||
// skip handling if shutting down
|
// skip handling if shutting down
|
||||||
if (isShutDownStarted) return;
|
if (isShutDownStarted) return;
|
||||||
|
|
||||||
// fallback to provided or public nodes if custom connection fails on startup
|
// invoke fallback handling on startup error
|
||||||
if (lastInfo == null && "".equals(config.xmrNode) && preferences.getMoneroNodesOption() == XmrNodes.MoneroNodesOption.CUSTOM) {
|
boolean canFallback = isFixedConnection() || isCustomConnections();
|
||||||
if (xmrNodes.getProvidedXmrNodes().isEmpty()) {
|
if (lastInfo == null && canFallback) {
|
||||||
log.warn("Failed to fetch daemon info from custom node on startup, falling back to public nodes: " + e.getMessage());
|
if (!connectionServiceFallbackHandlerActive.get() && (lastFallbackInvocation == null || System.currentTimeMillis() - lastFallbackInvocation > FALLBACK_INVOCATION_PERIOD_MS)) {
|
||||||
preferences.setMoneroNodesOptionOrdinal(XmrNodes.MoneroNodesOption.PUBLIC.ordinal());
|
log.warn("Failed to fetch daemon info from custom connection on startup: " + e.getMessage());
|
||||||
} else {
|
lastFallbackInvocation = System.currentTimeMillis();
|
||||||
log.warn("Failed to fetch daemon info from custom node on startup, falling back to provided nodes: " + e.getMessage());
|
connectionServiceFallbackHandlerActive.set(true);
|
||||||
preferences.setMoneroNodesOptionOrdinal(XmrNodes.MoneroNodesOption.PROVIDED.ordinal());
|
|
||||||
}
|
}
|
||||||
initializeConnections();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -819,6 +835,10 @@ public final class XmrConnectionService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isFixedConnection() {
|
private boolean isFixedConnection() {
|
||||||
return !"".equals(config.xmrNode) || preferences.getMoneroNodesOption() == XmrNodes.MoneroNodesOption.CUSTOM;
|
return !"".equals(config.xmrNode) && !fallbackApplied;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCustomConnections() {
|
||||||
|
return preferences.getMoneroNodesOption() == XmrNodes.MoneroNodesOption.CUSTOM;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,7 @@ public class HavenoHeadlessApp implements HeadlessApp {
|
||||||
log.info("onDisplayTacHandler: We accept the tacs automatically in headless mode");
|
log.info("onDisplayTacHandler: We accept the tacs automatically in headless mode");
|
||||||
acceptedHandler.run();
|
acceptedHandler.run();
|
||||||
});
|
});
|
||||||
|
havenoSetup.setDisplayMoneroConnectionFallbackHandler(show -> log.info("onDisplayMoneroConnectionFallbackHandler: show={}", show));
|
||||||
havenoSetup.setDisplayTorNetworkSettingsHandler(show -> log.info("onDisplayTorNetworkSettingsHandler: show={}", show));
|
havenoSetup.setDisplayTorNetworkSettingsHandler(show -> log.info("onDisplayTorNetworkSettingsHandler: show={}", show));
|
||||||
havenoSetup.setChainFileLockedExceptionHandler(msg -> log.error("onChainFileLockedExceptionHandler: msg={}", msg));
|
havenoSetup.setChainFileLockedExceptionHandler(msg -> log.error("onChainFileLockedExceptionHandler: msg={}", msg));
|
||||||
tradeManager.setLockedUpFundsHandler(msg -> log.info("onLockedUpFundsHandler: msg={}", msg));
|
tradeManager.setLockedUpFundsHandler(msg -> log.info("onLockedUpFundsHandler: msg={}", msg));
|
||||||
|
|
|
@ -158,6 +158,9 @@ public class HavenoSetup {
|
||||||
rejectedTxErrorMessageHandler;
|
rejectedTxErrorMessageHandler;
|
||||||
@Setter
|
@Setter
|
||||||
@Nullable
|
@Nullable
|
||||||
|
private Consumer<Boolean> displayMoneroConnectionFallbackHandler;
|
||||||
|
@Setter
|
||||||
|
@Nullable
|
||||||
private Consumer<Boolean> displayTorNetworkSettingsHandler;
|
private Consumer<Boolean> displayTorNetworkSettingsHandler;
|
||||||
@Setter
|
@Setter
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -426,6 +429,12 @@ public class HavenoSetup {
|
||||||
getXmrDaemonSyncProgress().addListener((observable, oldValue, newValue) -> resetStartupTimeout());
|
getXmrDaemonSyncProgress().addListener((observable, oldValue, newValue) -> resetStartupTimeout());
|
||||||
getXmrWalletSyncProgress().addListener((observable, oldValue, newValue) -> resetStartupTimeout());
|
getXmrWalletSyncProgress().addListener((observable, oldValue, newValue) -> resetStartupTimeout());
|
||||||
|
|
||||||
|
// listen for fallback handling
|
||||||
|
getConnectionServiceFallbackHandlerActive().addListener((observable, oldValue, newValue) -> {
|
||||||
|
if (displayMoneroConnectionFallbackHandler == null) return;
|
||||||
|
displayMoneroConnectionFallbackHandler.accept(newValue);
|
||||||
|
});
|
||||||
|
|
||||||
log.info("Init P2P network");
|
log.info("Init P2P network");
|
||||||
havenoSetupListeners.forEach(HavenoSetupListener::onInitP2pNetwork);
|
havenoSetupListeners.forEach(HavenoSetupListener::onInitP2pNetwork);
|
||||||
p2pNetworkReady = p2PNetworkSetup.init(this::initWallet, displayTorNetworkSettingsHandler);
|
p2pNetworkReady = p2PNetworkSetup.init(this::initWallet, displayTorNetworkSettingsHandler);
|
||||||
|
@ -725,6 +734,10 @@ public class HavenoSetup {
|
||||||
return xmrConnectionService.getConnectionServiceErrorMsg();
|
return xmrConnectionService.getConnectionServiceErrorMsg();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BooleanProperty getConnectionServiceFallbackHandlerActive() {
|
||||||
|
return xmrConnectionService.getConnectionServiceFallbackHandlerActive();
|
||||||
|
}
|
||||||
|
|
||||||
public StringProperty getTopErrorMsg() {
|
public StringProperty getTopErrorMsg() {
|
||||||
return topErrorMsg;
|
return topErrorMsg;
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,7 +120,7 @@ public class WalletAppSetup {
|
||||||
@Nullable Runnable showPopupIfInvalidBtcConfigHandler,
|
@Nullable Runnable showPopupIfInvalidBtcConfigHandler,
|
||||||
Runnable downloadCompleteHandler,
|
Runnable downloadCompleteHandler,
|
||||||
Runnable walletInitializedHandler) {
|
Runnable walletInitializedHandler) {
|
||||||
log.info("Initialize WalletAppSetup with monero-java version {}", MoneroUtils.getVersion());
|
log.info("Initialize WalletAppSetup with monero-java v{}", MoneroUtils.getVersion());
|
||||||
|
|
||||||
ObjectProperty<Throwable> walletServiceException = new SimpleObjectProperty<>();
|
ObjectProperty<Throwable> walletServiceException = new SimpleObjectProperty<>();
|
||||||
xmrInfoBinding = EasyBind.combine(
|
xmrInfoBinding = EasyBind.combine(
|
||||||
|
|
|
@ -1298,9 +1298,10 @@ public class XmrWalletService extends XmrWalletBase {
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// force restart main wallet if connection changed while syncing
|
// force restart main wallet if connection changed while syncing
|
||||||
log.warn("Force restarting main wallet because connection changed while syncing");
|
if (wallet != null) {
|
||||||
forceRestartMainWallet();
|
log.warn("Force restarting main wallet because connection changed while syncing");
|
||||||
return;
|
forceRestartMainWallet();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2044,6 +2044,9 @@ closedTradesSummaryWindow.totalTradeFeeInXmr.title=Sum of all trade fees paid in
|
||||||
closedTradesSummaryWindow.totalTradeFeeInXmr.value={0} ({1} of total trade amount)
|
closedTradesSummaryWindow.totalTradeFeeInXmr.value={0} ({1} of total trade amount)
|
||||||
walletPasswordWindow.headline=Enter password to unlock
|
walletPasswordWindow.headline=Enter password to unlock
|
||||||
|
|
||||||
|
connectionFallback.headline=Connection error
|
||||||
|
connectionFallback.msg=Error connecting to your custom Monero node(s).\n\nDo you want to try the next best available Monero node?
|
||||||
|
|
||||||
torNetworkSettingWindow.header=Tor networks settings
|
torNetworkSettingWindow.header=Tor networks settings
|
||||||
torNetworkSettingWindow.noBridges=Don't use bridges
|
torNetworkSettingWindow.noBridges=Don't use bridges
|
||||||
torNetworkSettingWindow.providedBridges=Connect with provided bridges
|
torNetworkSettingWindow.providedBridges=Connect with provided bridges
|
||||||
|
|
|
@ -674,6 +674,7 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
xmrInfoLabel.setId("footer-pane");
|
xmrInfoLabel.setId("footer-pane");
|
||||||
|
xmrInfoLabel.getStyleClass().remove("error-text");
|
||||||
if (xmrNetworkWarnMsgPopup != null)
|
if (xmrNetworkWarnMsgPopup != null)
|
||||||
xmrNetworkWarnMsgPopup.hide();
|
xmrNetworkWarnMsgPopup.hide();
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,6 +140,7 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
|
||||||
@SuppressWarnings("FieldCanBeLocal")
|
@SuppressWarnings("FieldCanBeLocal")
|
||||||
private MonadicBinding<Boolean> tradesAndUIReady;
|
private MonadicBinding<Boolean> tradesAndUIReady;
|
||||||
private final Queue<Overlay<?>> popupQueue = new PriorityQueue<>(Comparator.comparing(Overlay::getDisplayOrderPriority));
|
private final Queue<Overlay<?>> popupQueue = new PriorityQueue<>(Comparator.comparing(Overlay::getDisplayOrderPriority));
|
||||||
|
private Popup moneroConnectionFallbackPopup;
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -334,9 +335,38 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
|
||||||
tacWindow.onAction(acceptedHandler::run).show();
|
tacWindow.onAction(acceptedHandler::run).show();
|
||||||
}, 1));
|
}, 1));
|
||||||
|
|
||||||
|
havenoSetup.setDisplayMoneroConnectionFallbackHandler(show -> {
|
||||||
|
if (moneroConnectionFallbackPopup == null) {
|
||||||
|
moneroConnectionFallbackPopup = new Popup()
|
||||||
|
.headLine(Res.get("connectionFallback.headline"))
|
||||||
|
.warning(Res.get("connectionFallback.msg"))
|
||||||
|
.closeButtonText(Res.get("shared.no"))
|
||||||
|
.actionButtonText(Res.get("shared.yes"))
|
||||||
|
.onAction(() -> {
|
||||||
|
havenoSetup.getConnectionServiceFallbackHandlerActive().set(false);
|
||||||
|
new Thread(() -> HavenoUtils.xmrConnectionService.fallbackToBestConnection()).start();
|
||||||
|
})
|
||||||
|
.onClose(() -> {
|
||||||
|
log.warn("User has declined to fallback to the next best available Monero node.");
|
||||||
|
havenoSetup.getConnectionServiceFallbackHandlerActive().set(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (show) {
|
||||||
|
moneroConnectionFallbackPopup.show();
|
||||||
|
} else if (moneroConnectionFallbackPopup.isDisplayed()) {
|
||||||
|
moneroConnectionFallbackPopup.hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
havenoSetup.setDisplayTorNetworkSettingsHandler(show -> {
|
havenoSetup.setDisplayTorNetworkSettingsHandler(show -> {
|
||||||
if (show) {
|
if (show) {
|
||||||
torNetworkSettingsWindow.show();
|
torNetworkSettingsWindow.show();
|
||||||
|
|
||||||
|
// bring connection fallback popup to front if displayed
|
||||||
|
if (moneroConnectionFallbackPopup != null && moneroConnectionFallbackPopup.isDisplayed()) {
|
||||||
|
moneroConnectionFallbackPopup.hide();
|
||||||
|
moneroConnectionFallbackPopup.show();
|
||||||
|
}
|
||||||
} else if (torNetworkSettingsWindow.isDisplayed()) {
|
} else if (torNetworkSettingsWindow.isDisplayed()) {
|
||||||
torNetworkSettingsWindow.hide();
|
torNetworkSettingsWindow.hide();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue