Skip to content
Permalink
Browse files

SimpleMode: automatic public nodes discovering and switching

  • Loading branch information...
xiphon committed Sep 2, 2019
1 parent a5b2d5e commit 0d247a9b8a2fa6770d1834584eedb109c6ab7e3d
@@ -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);
}
@@ -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,15 +1390,13 @@ 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
property int kdfRounds: 1
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);
}
const sinceLastConnect = Utils.epoch() - appWindow.disconnectedEpoch;
if (sinceLastConnect < disconnectedTimeoutSec && checkNoSyncFlag()) {
return;
}

// reset state
appWindow.disconnectedEpoch = 0;
return;
}, function(){
appWindow.showStatusMessage(qsTr("Failed to fetch remote nodes from third-party server."), simpleModeConnectionTimer.interval / 1000);
});
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"));
}
@@ -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] {
@@ -44,9 +44,10 @@ class DaemonManager : public QObject

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 @@ public slots:
QString m_monerod;
bool m_has_daemon = true;
bool m_app_exit = false;
bool m_noSync = false;

mutable FutureScheduler m_scheduler;
};
@@ -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: {
//
}
@@ -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";
}

0 comments on commit 0d247a9

Please sign in to comment.
You can’t perform that action at this time.