From 0d247a9b8a2fa6770d1834584eedb109c6ab7e3d Mon Sep 17 00:00:00 2001 From: xiphon Date: Mon, 2 Sep 2019 21:32:36 +0000 Subject: [PATCH] SimpleMode: automatic public nodes discovering and switching --- js/Utils.js | 10 --- main.qml | 93 +++++++++++++------------- src/daemon/DaemonManager.cpp | 19 ++++-- src/daemon/DaemonManager.h | 4 +- wizard/WizardController.qml | 83 ----------------------- wizard/WizardCreateWallet2.qml | 7 +- wizard/WizardModeBootstrap.qml | 61 ----------------- wizard/WizardModeRemoteNodeWarning.qml | 61 ----------------- wizard/WizardOpenWallet1.qml | 11 +-- wizard/WizardRestoreWallet2.qml | 7 +- 10 files changed, 67 insertions(+), 289 deletions(-) diff --git a/js/Utils.js b/js/Utils.js index 4a9a5fed..96786650 100644 --- a/js/Utils.js +++ b/js/Utils.js @@ -94,16 +94,6 @@ function netTypeToString(){ return nettype == 1 ? qsTr("Testnet") : nettype == 2 ? qsTr("Stagenet") : qsTr("Mainnet"); } -function randomChoice(arr){ - return arr[Math.floor(Math.random() * arr.length)]; -} - -function filterNodes(nodes, port) { - if(typeof data === 'number') - port = port.toString(); - return nodes.filter(function(_){return _.indexOf(port) !== -1}); -} - function epoch(){ return Math.floor((new Date).getTime()/1000); } diff --git a/main.qml b/main.qml index fadf93f0..3c1e2520 100644 --- a/main.qml +++ b/main.qml @@ -106,19 +106,6 @@ ApplicationWindow { } } - property string remoteNodeService: { - // support user-defined remote node aggregators - if(persistentSettings.remoteNodeService){ - var service = persistentSettings.remoteNodeService; - if(service.charAt(service.length-1) !== "/") - service += "/"; - return service; - } - - // monero-gui workgroup maintained - return "https://autonode.xmr.pm/" - } - // true if wallet ever synchronized property bool walletInitialized : false @@ -705,7 +692,8 @@ ApplicationWindow { simpleModeConnectionTimer.stop(); appWindow.showProcessingSplash(qsTr("Waiting for daemon to start...")) - daemonManager.start(flags, persistentSettings.nettype, persistentSettings.blockchainDataDir, persistentSettings.bootstrapNodeAddress); + const noSync = appWindow.walletMode === 0; + daemonManager.start(flags, persistentSettings.nettype, persistentSettings.blockchainDataDir, persistentSettings.bootstrapNodeAddress, noSync); persistentSettings.daemonFlags = flags } @@ -1402,7 +1390,6 @@ ApplicationWindow { property bool useRemoteNode: false property string remoteNodeAddress: "" property string bootstrapNodeAddress: "" - property string remoteNodeRegion: "" property bool segregatePreForkOutputs: true property bool keyReuseMitigation2: true property int segregationHeight: 0 @@ -1410,7 +1397,6 @@ ApplicationWindow { property bool hideBalance: false property bool lockOnUserInActivity: true property int walletMode: 2 - property string remoteNodeService: "" property int lockOnUserInActivityInterval: 10 // minutes property bool showPid: false property bool blackTheme: true @@ -2023,41 +2009,44 @@ ApplicationWindow { onTriggered: appWindow.themeTransition = true; } + function checkNoSyncFlag() { + if (!appWindow.daemonRunning) { + return true; + } + if (appWindow.walletMode == 0 && !daemonManager.noSync()) { + return false; + } + if (appWindow.walletMode == 1 && daemonManager.noSync()) { + return false; + } + return true; + } + function checkSimpleModeConnection(){ // auto-connection mechanism for simple mode - if(persistentSettings.nettype != NetworkType.MAINNET) return; if(appWindow.walletMode >= 2) return; - var disconnected = leftPanel.networkStatus.connected === Wallet.ConnectionStatus_Disconnected; - var disconnectedEpoch = appWindow.disconnectedEpoch; - if(disconnectedEpoch === 0){ + const disconnectedTimeoutSec = 30; + const firstCheckDelaySec = 2; + + const connected = leftPanel.networkStatus.connected === Wallet.ConnectionStatus_Connected; + const firstRun = appWindow.disconnectedEpoch == 0; + if (firstRun) { + appWindow.disconnectedEpoch = Utils.epoch() + firstCheckDelaySec - disconnectedTimeoutSec; + } else if (connected) { appWindow.disconnectedEpoch = Utils.epoch(); } - // disconnected longer than 5 seconds? - if(disconnected && disconnectedEpoch > 0 && (Utils.epoch() - disconnectedEpoch) >= 5){ - // for bootstrap mode, first wait until daemon is killed - if(appWindow.walletMode === 1 && appWindow.daemonRunning) { - appWindow.stopDaemon(); - return; - } - - // fetch new node list - wizard.fetchRemoteNodes(function() { - // fetched node, connect - if(appWindow.walletMode === 0){ - appWindow.connectRemoteNode(); - } else if(appWindow.walletMode === 1){ - appWindow.startDaemon(persistentSettings.daemonFlags); - } - - // reset state - appWindow.disconnectedEpoch = 0; - return; - }, function(){ - appWindow.showStatusMessage(qsTr("Failed to fetch remote nodes from third-party server."), simpleModeConnectionTimer.interval / 1000); - }); + const sinceLastConnect = Utils.epoch() - appWindow.disconnectedEpoch; + if (sinceLastConnect < disconnectedTimeoutSec && checkNoSyncFlag()) { + return; } + + if (appWindow.daemonRunning) { + appWindow.stopDaemon(); + } + appWindow.startDaemon(persistentSettings.daemonFlags); + appWindow.disconnectedEpoch = Utils.epoch(); } Timer { @@ -2136,7 +2125,12 @@ ApplicationWindow { closeAccepted(); }; - confirmationDialog.open() + if (appWindow.walletMode == 0) { + stopDaemon(); + closeAccepted(); + } else { + confirmationDialog.open(); + } } else { closeAccepted(); @@ -2248,11 +2242,16 @@ ApplicationWindow { } function changeWalletMode(mode){ + appWindow.disconnectedEpoch = 0; appWindow.walletMode = mode; persistentSettings.walletMode = mode; - persistentSettings.useRemoteNode = mode === 0 ? true : false; - if (mode < 2 && (middlePanel.settingsView.settingsStateViewState === "Node" || middlePanel.settingsView.settingsStateViewState === "Log")) { - middlePanel.settingsView.settingsStateViewState = "Wallet" + if (mode < 2) { + persistentSettings.useRemoteNode = false; + persistentSettings.bootstrapNodeAddress = "auto"; + + if (middlePanel.settingsView.settingsStateViewState === "Node" || middlePanel.settingsView.settingsStateViewState === "Log") { + middlePanel.settingsView.settingsStateViewState = "Wallet" + } } console.log("walletMode changed: " + (mode === 0 ? "simple": mode === 1 ? "simple (bootstrap)" : "Advanced")); } diff --git a/src/daemon/DaemonManager.cpp b/src/daemon/DaemonManager.cpp index 4277e0bb..29a7086d 100644 --- a/src/daemon/DaemonManager.cpp +++ b/src/daemon/DaemonManager.cpp @@ -61,7 +61,7 @@ DaemonManager *DaemonManager::instance(const QStringList *args) return m_instance; } -bool DaemonManager::start(const QString &flags, NetworkType::Type nettype, const QString &dataDir, const QString &bootstrapNodeAddress) +bool DaemonManager::start(const QString &flags, NetworkType::Type nettype, const QString &dataDir, const QString &bootstrapNodeAddress, bool noSync /* = false*/) { // prepare command line arguments and pass to monerod QStringList arguments; @@ -99,6 +99,10 @@ bool DaemonManager::start(const QString &flags, NetworkType::Type nettype, const arguments << "--bootstrap-daemon-address" << bootstrapNodeAddress; } + if (noSync) { + arguments << "--no-sync"; + } + arguments << "--check-updates" << "disabled"; // --max-concurrency based on threads available. max: 6 @@ -131,11 +135,13 @@ bool DaemonManager::start(const QString &flags, NetworkType::Type nettype, const } // Start start watcher - m_scheduler.run([this, nettype] { - if (startWatcher(nettype)) + m_scheduler.run([this, nettype, noSync] { + if (startWatcher(nettype)) { emit daemonStarted(); - else + m_noSync = noSync; + } else { emit daemonStartFailure(); + } }); return true; @@ -244,6 +250,11 @@ bool DaemonManager::running(NetworkType::Type nettype) const return false; } +bool DaemonManager::noSync() const noexcept +{ + return m_noSync; +} + void DaemonManager::runningAsync(NetworkType::Type nettype, const QJSValue& callback) const { m_scheduler.run([this, nettype] { diff --git a/src/daemon/DaemonManager.h b/src/daemon/DaemonManager.h index 5a29ea64..0df97c63 100644 --- a/src/daemon/DaemonManager.h +++ b/src/daemon/DaemonManager.h @@ -44,9 +44,10 @@ public: static DaemonManager * instance(const QStringList *args); - Q_INVOKABLE bool start(const QString &flags, NetworkType::Type nettype, const QString &dataDir = "", const QString &bootstrapNodeAddress = ""); + Q_INVOKABLE bool start(const QString &flags, NetworkType::Type nettype, const QString &dataDir = "", const QString &bootstrapNodeAddress = "", bool noSync = false); Q_INVOKABLE bool stop(NetworkType::Type nettype); + Q_INVOKABLE bool noSync() const noexcept; // return true if daemon process is started Q_INVOKABLE void runningAsync(NetworkType::Type nettype, const QJSValue& callback) const; // Send daemon command from qml and prints output in console window. @@ -82,6 +83,7 @@ private: QString m_monerod; bool m_has_daemon = true; bool m_app_exit = false; + bool m_noSync = false; mutable FutureScheduler m_scheduler; }; diff --git a/wizard/WizardController.qml b/wizard/WizardController.qml index 7d5b662f..d1d29a5b 100644 --- a/wizard/WizardController.qml +++ b/wizard/WizardController.qml @@ -72,7 +72,6 @@ Rectangle { wizardController.tmpWalletFilename = ''; wizardController.walletRestoreMode = 'seed' wizardController.walletOptionsSubaddressLookahead = ''; - wizardController.remoteNodes = {}; disconnect(); if (typeof wizardController.m_wallet !== 'undefined'){ @@ -107,7 +106,6 @@ Rectangle { property string walletOptionsDeviceName: '' property bool walletOptionsDeviceIsRestore: false property string tmpWalletFilename: '' - property var remoteNodes: '' // language settings, updated via sidebar property string language_locale: 'en_US' @@ -560,87 +558,6 @@ Rectangle { passwordDialog.open(appWindow.usefulName(appWindow.walletPath())); } - function fetchRemoteNodes(cb, cb_err){ - // Fetch remote nodes, parse JSON, store in result `wizardController.remoteNodes`, call setAutNode(), call callback - var url = appWindow.remoteNodeService + 'api/nodes.json'; - console.log("HTTP request: " + url); - - var xhr = new XMLHttpRequest(); - xhr.timeout = 3500; - - // Unfortunately we cannot spoof User-Agent since it is hardcoded in Qt - //xhr.setRequestHeader("User-Agent", "-"); - - xhr.onreadystatechange = function() { - var msg; - if (xhr.readyState != 4) { - return; - } else if(xhr.status != 200){ - msg = "Error fetching remote nodes; status code was not 200"; - console.log(msg); - if(typeof cb_err === 'function') - return cb_err(msg); - } else { - var body = xhr.responseText; - if(typeof body === 'undefined' || body === ''){ - msg = "Error fetching remote nodes; response body was empty"; - console.log(msg); - if(typeof cb_err === 'function') - return cb_err(msg); - } - - var data = JSON.parse(body); - wizardController.remoteNodes = data; - console.log("node list updated"); - setAutoNode(); - return cb(); - } - } - - xhr.open('GET', url, true); - xhr.send(null); - } - - function setAutoNode(){ - var node; - var nodes; - var nodeObject = wizardController.remoteNodes; - var region = persistentSettings.remoteNodeRegion; - - if(typeof region !== 'undefined' && region !== ""){ - if(nodeObject.hasOwnProperty(region) && nodeObject[region].length > 0){ - nodes = nodeObject[region]; - } else { - console.log("No suitable nodes found for region " + region + ". Defaulting to random node."); - } - } - - if(typeof nodes === 'undefined'){ - nodes = []; - Object.keys(nodeObject).forEach(function(obj, i){ - nodes = nodes.concat(nodeObject[obj]); - }); - } - - // 18089 has precedence - var filteredNodes = Utils.filterNodes(nodes, "18089"); - if(filteredNodes.length > 0){ - node = Utils.randomChoice(filteredNodes); - console.log('Choosing remote node \''+ node +'\' from a list of ' + filteredNodes.length); - } else if(nodes.length > 0){ - node = Utils.randomChoice(nodes); - console.log('Choosing remote node \''+ node +'\' from a list of ' + nodes.length); - } else { - console.log("No suitable nodes found.") - return '' - } - - if(appWindow.walletMode === 0) - persistentSettings.remoteNodeAddress = node; - else if(appWindow.walletMode === 1) - persistentSettings.bootstrapNodeAddress = node; - } - Component.onCompleted: { // } diff --git a/wizard/WizardCreateWallet2.qml b/wizard/WizardCreateWallet2.qml index 7c698ee9..9df4680a 100644 --- a/wizard/WizardCreateWallet2.qml +++ b/wizard/WizardCreateWallet2.qml @@ -74,12 +74,7 @@ Rectangle { wizardController.walletOptionsPassword = passwordFields.password; if(appWindow.walletMode === 0 || appWindow.walletMode === 1){ - wizardController.fetchRemoteNodes(function(){ - wizardStateView.state = "wizardCreateWallet4"; - }, function(){ - appWindow.showStatusMessage(qsTr("Failed to fetch remote nodes from third-party server."), 5); - wizardStateView.state = "wizardCreateWallet4"; - }); + wizardStateView.state = "wizardCreateWallet4"; } else { wizardStateView.state = "wizardCreateWallet3"; } diff --git a/wizard/WizardModeBootstrap.qml b/wizard/WizardModeBootstrap.qml index ba911d63..5b583101 100644 --- a/wizard/WizardModeBootstrap.qml +++ b/wizard/WizardModeBootstrap.qml @@ -97,49 +97,6 @@ Rectangle { text: qsTr("Remain aware of these limitations. Users who prioritize privacy and decentralization must use a full node instead.") + translationManager.emptyString } - MoneroComponents.TextPlain { - text: qsTr("For enhanced node performance you may specify your region:") + translationManager.emptyString - wrapMode: Text.Wrap - Layout.topMargin: 8 - Layout.fillWidth: true - - font.family: MoneroComponents.Style.fontRegular.name - font.pixelSize: 16 - color: MoneroComponents.Style.defaultFontColor - } - - GridLayout { - columns: 3 - columnSpacing: 20 - - ColumnLayout { - Layout.fillWidth: true - spacing: 0 - - MoneroComponents.StandardDropdown { - id: regionDropdown - Layout.fillWidth: true - dataModel: regionModel - currentIndex: 0 - - onChanged: { - var region = regionModel.get(currentIndex).region; - persistentSettings.remoteNodeRegion = region; - } - } - } - - Item { - Layout.fillWidth: true - } - - Item { - Layout.fillWidth: true - } - - z: parent.z + 1 - } - MoneroComponents.CheckBox { id: understoodCheckbox Layout.topMargin: 20 @@ -184,22 +141,4 @@ Rectangle { wizardModeBootstrapWarning.understood = false; understoodCheckbox.checked = false; } - - Component.onCompleted: { - var region = persistentSettings.remoteNodeRegion; - - if(region){ - for(var i = 0; i !== regionDropdown.dataModel.count; i++){ - var item = regionDropdown.dataModel.get(i); - if(item['region'] === region){ - regionDropdown.currentIndex = i; - break; - } - } - } else { - regionDropdown.currentIndex = 0; - } - - regionDropdown.update(); - } } diff --git a/wizard/WizardModeRemoteNodeWarning.qml b/wizard/WizardModeRemoteNodeWarning.qml index d6c2dd1d..be53b168 100644 --- a/wizard/WizardModeRemoteNodeWarning.qml +++ b/wizard/WizardModeRemoteNodeWarning.qml @@ -100,49 +100,6 @@ Rectangle { text: qsTr("Remain aware of these limitations. Users who prioritize privacy and decentralization must use a full node instead.") + translationManager.emptyString } - MoneroComponents.TextPlain { - text: qsTr("For enhanced node performance you may specify your region:") + translationManager.emptyString - wrapMode: Text.Wrap - Layout.topMargin: 8 - Layout.fillWidth: true - - font.family: MoneroComponents.Style.fontRegular.name - font.pixelSize: 16 - color: MoneroComponents.Style.defaultFontColor - } - - GridLayout { - columns: 3 - columnSpacing: 20 - - ColumnLayout { - Layout.fillWidth: true - spacing: 0 - - MoneroComponents.StandardDropdown { - id: regionDropdown - Layout.fillWidth: true - dataModel: regionModel - currentIndex: 0 - - onChanged: { - var region = regionModel.get(currentIndex).region; - persistentSettings.remoteNodeRegion = region; - } - } - } - - Item { - Layout.fillWidth: true - } - - Item { - Layout.fillWidth: true - } - - z: parent.z + 1 - } - MoneroComponents.CheckBox { id: understoodCheckbox Layout.topMargin: 20 @@ -187,22 +144,4 @@ Rectangle { wizardModeRemoteNodeWarning.understood = false; understoodCheckbox.checked = false; } - - Component.onCompleted: { - var region = persistentSettings.remoteNodeRegion; - - if(region){ - for(var i = 0; i !== regionDropdown.dataModel.count; i++){ - var item = regionDropdown.dataModel.get(i); - if(item['region'] === region){ - regionDropdown.currentIndex = i; - break; - } - } - } else { - regionDropdown.currentIndex = 0; - } - - regionDropdown.update(); - } } diff --git a/wizard/WizardOpenWallet1.qml b/wizard/WizardOpenWallet1.qml index 4d4d1ed7..e0d93a05 100644 --- a/wizard/WizardOpenWallet1.qml +++ b/wizard/WizardOpenWallet1.qml @@ -282,16 +282,7 @@ Rectangle { onClicked: { persistentSettings.nettype = parseInt(networktype) - if(appWindow.walletMode === 0 || appWindow.walletMode === 1){ - wizardController.fetchRemoteNodes(function(){ - wizardController.openWalletFile(item.filePath); - }, function(){ - appWindow.showStatusMessage(qsTr("Failed to fetch remote nodes from third-party server."), 5); - wizardController.openWalletFile(item.filePath); - }); - } else { - wizardController.openWalletFile(item.filePath); - } + wizardController.openWalletFile(item.filePath); } } } diff --git a/wizard/WizardRestoreWallet2.qml b/wizard/WizardRestoreWallet2.qml index 161421c7..8e270984 100644 --- a/wizard/WizardRestoreWallet2.qml +++ b/wizard/WizardRestoreWallet2.qml @@ -73,12 +73,7 @@ Rectangle { wizardController.walletOptionsPassword = passwordFields.password; if(appWindow.walletMode === 0 || appWindow.walletMode === 1){ - wizardController.fetchRemoteNodes(function(){ - wizardStateView.state = "wizardRestoreWallet4"; - }, function(){ - appWindow.showStatusMessage(qsTr("Failed to fetch remote nodes from third-party server."), 5); - wizardStateView.state = "wizardRestoreWallet4"; - }); + wizardStateView.state = "wizardRestoreWallet4"; } else { wizardStateView.state = "wizardRestoreWallet3"; }