diff --git a/CMakeLists.txt b/CMakeLists.txt
index abb160f..c9806b4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -31,7 +31,7 @@ if(DEBUG)
set(CMAKE_VERBOSE_MAKEFILE ON)
endif()
-set(MONERO_HEAD "2390030d10b69c357165f82aaf417391a9e11019")
+set(MONERO_HEAD "2fc0c6355d7f3756f9cc01f1165aeec42bc52201")
set(BUILD_GUI_DEPS ON)
set(ARCH "x86-64")
set(BUILD_64 ON)
diff --git a/monero b/monero
index 2390030..2fc0c63 160000
--- a/monero
+++ b/monero
@@ -1 +1 @@
-Subproject commit 2390030d10b69c357165f82aaf417391a9e11019
+Subproject commit 2fc0c6355d7f3756f9cc01f1165aeec42bc52201
diff --git a/src/appcontext.cpp b/src/appcontext.cpp
index 5017164..f8ce281 100644
--- a/src/appcontext.cpp
+++ b/src/appcontext.cpp
@@ -113,7 +113,6 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
this->nodes = new Nodes(this, this->networkClearnet);
connect(this, &AppContext::nodeSourceChanged, this->nodes, &Nodes::onNodeSourceChanged);
connect(this, &AppContext::setCustomNodes, this->nodes, &Nodes::setCustomNodes);
- connect(this, &AppContext::walletClosing, this->nodes, &Nodes::onWalletClosing);
// Tor & socks proxy
this->ws = new WSClient(this, m_wsUrl);
@@ -261,7 +260,6 @@ void AppContext::onCreateTransactionError(const QString &msg) {
}
void AppContext::walletClose(bool emitClosedSignal) {
- this->nodes->stopTimer();
if(this->currentWallet == nullptr) return;
emit walletClosing();
//ctx->currentWallet->store(); @TODO: uncomment to store on wallet close
@@ -341,6 +339,9 @@ void AppContext::onWalletOpened(Wallet *wallet) {
emit walletOpened();
+ connect(this->currentWallet, &Wallet::connectionStatusChanged, [this]{
+ this->nodes->autoConnect();
+ });
this->nodes->connectToNode();
this->updateBalance();
@@ -723,13 +724,15 @@ void AppContext::onWalletUpdate() {
this->storeWallet();
}
-void AppContext::onWalletRefreshed() {
+void AppContext::onWalletRefreshed(bool success) {
if (!this->refreshed) {
refreshModels();
this->refreshed = true;
this->storeWallet();
}
+ qDebug() << "Wallet refresh status: " << success;
+
this->currentWallet->refreshHeightAsync();
}
@@ -746,7 +749,7 @@ void AppContext::onWalletNewBlock(quint64 blockheight, quint64 targetHeight) {
void AppContext::onHeightRefreshed(quint64 walletHeight, quint64 daemonHeight, quint64 targetHeight) {
qDebug() << Q_FUNC_INFO << walletHeight << daemonHeight << targetHeight;
- if (!this->currentWallet->connected())
+ if (this->currentWallet->connectionStatus() == Wallet::ConnectionStatus_Disconnected)
return;
if (daemonHeight < targetHeight) {
diff --git a/src/appcontext.h b/src/appcontext.h
index f4d346d..b2c58ad 100644
--- a/src/appcontext.h
+++ b/src/appcontext.h
@@ -126,7 +126,7 @@ private slots:
void onMoneyReceived(const QString &txId, quint64 amount);
void onUnconfirmedMoneyReceived(const QString &txId, quint64 amount);
void onWalletUpdate();
- void onWalletRefreshed();
+ void onWalletRefreshed(bool success);
void onWalletOpened(Wallet *wallet);
void onWalletNewBlock(quint64 blockheight, quint64 targetHeight);
void onHeightRefreshed(quint64 walletHeight, quint64 daemonHeight, quint64 targetHeight);
diff --git a/src/dialog/debuginfodialog.cpp b/src/dialog/debuginfodialog.cpp
index 7363387..d3b00a7 100644
--- a/src/dialog/debuginfodialog.cpp
+++ b/src/dialog/debuginfodialog.cpp
@@ -12,15 +12,26 @@
DebugInfoDialog::DebugInfoDialog(AppContext *ctx, QWidget *parent)
: QDialog(parent)
, ui(new Ui::DebugInfoDialog)
+ , m_ctx(ctx)
{
ui->setupUi(this);
+ connect(ui->btn_Copy, &QPushButton::clicked, this, &DebugInfoDialog::copyToClipboad);
+
+ m_updateTimer.start(5000);
+ connect(&m_updateTimer, &QTimer::timeout, this, &DebugInfoDialog::updateInfo);
+ this->updateInfo();
+
+ this->adjustSize();
+}
+
+void DebugInfoDialog::updateInfo() {
QString torStatus;
- if(ctx->isTorSocks)
+ if(m_ctx->isTorSocks)
torStatus = "Torsocks";
- else if(ctx->tor->localTor)
+ else if(m_ctx->tor->localTor)
torStatus = "Local (assumed to be running)";
- else if(ctx->tor->torConnected)
+ else if(m_ctx->tor->torConnected)
torStatus = "Running";
else
torStatus = "Unknown";
@@ -28,32 +39,28 @@ DebugInfoDialog::DebugInfoDialog(AppContext *ctx, QWidget *parent)
ui->label_featherVersion->setText(QString("%1-%2").arg(FEATHER_VERSION, FEATHER_BRANCH));
ui->label_moneroVersion->setText(QString("%1-%2").arg(MONERO_VERSION, MONERO_BRANCH));
- ui->label_walletHeight->setText(QString::number(ctx->currentWallet->blockChainHeight()));
- ui->label_daemonHeight->setText(QString::number(ctx->currentWallet->daemonBlockChainHeight()));
- ui->label_targetHeight->setText(QString::number(ctx->currentWallet->daemonBlockChainTargetHeight()));
- ui->label_restoreHeight->setText(QString::number(ctx->currentWallet->getWalletCreationHeight()));
- ui->label_synchronized->setText(ctx->currentWallet->synchronized() ? "True" : "False");
+ ui->label_walletHeight->setText(QString::number(m_ctx->currentWallet->blockChainHeight()));
+ ui->label_daemonHeight->setText(QString::number(m_ctx->currentWallet->daemonBlockChainHeight()));
+ ui->label_targetHeight->setText(QString::number(m_ctx->currentWallet->daemonBlockChainTargetHeight()));
+ ui->label_restoreHeight->setText(QString::number(m_ctx->currentWallet->getWalletCreationHeight()));
+ ui->label_synchronized->setText(m_ctx->currentWallet->synchronized() ? "True" : "False");
- auto node = ctx->nodes->connection();
+ auto node = m_ctx->nodes->connection();
ui->label_remoteNode->setText(node.full);
- ui->label_walletStatus->setText(this->statusToString(ctx->currentWallet->connected()));
+ ui->label_walletStatus->setText(this->statusToString(m_ctx->currentWallet->connectionStatus()));
ui->label_torStatus->setText(torStatus);
- ui->label_websocketStatus->setText(Utils::QtEnumToString(ctx->ws->webSocket.state()));
+ ui->label_websocketStatus->setText(Utils::QtEnumToString(m_ctx->ws->webSocket.state()));
- ui->label_netType->setText(Utils::QtEnumToString(ctx->currentWallet->nettype()));
- ui->label_seedType->setText(ctx->currentWallet->getCacheAttribute("feather.seed").isEmpty() ? "25 word" : "14 word");
- ui->label_viewOnly->setText(ctx->currentWallet->viewOnly() ? "True" : "False");
+ ui->label_netType->setText(Utils::QtEnumToString(m_ctx->currentWallet->nettype()));
+ ui->label_seedType->setText(m_ctx->currentWallet->getCacheAttribute("feather.seed").isEmpty() ? "25 word" : "14 word");
+ ui->label_viewOnly->setText(m_ctx->currentWallet->viewOnly() ? "True" : "False");
QString os = QSysInfo::prettyProductName();
- if (ctx->isTails) {
+ if (m_ctx->isTails) {
os = QString("Tails %1").arg(TailsOS::version());
}
ui->label_OS->setText(os);
ui->label_timestamp->setText(QString::number(QDateTime::currentSecsSinceEpoch()));
-
- connect(ui->btn_Copy, &QPushButton::clicked, this, &DebugInfoDialog::copyToClipboad);
-
- this->adjustSize();
}
QString DebugInfoDialog::statusToString(Wallet::ConnectionStatus status) {
@@ -72,27 +79,28 @@ QString DebugInfoDialog::statusToString(Wallet::ConnectionStatus status) {
}
void DebugInfoDialog::copyToClipboad() {
+ // Two spaces at the end of each line are for newlines in Markdown
QString text = "";
- text += QString("Feather version: %1\n").arg(ui->label_featherVersion->text());
- text += QString("Monero version: %1\n").arg(ui->label_moneroVersion->text());
+ text += QString("Feather version: %1 \n").arg(ui->label_featherVersion->text());
+ text += QString("Monero version: %1 \n").arg(ui->label_moneroVersion->text());
- text += QString("Wallet height: %1\n").arg(ui->label_walletHeight->text());
- text += QString("Daemon height: %1\n").arg(ui->label_daemonHeight->text());
- text += QString("Target height: %1\n").arg(ui->label_targetHeight->text());
- text += QString("Restore height: %1\n").arg(ui->label_restoreHeight->text());
- text += QString("Synchronized: %1\n").arg(ui->label_synchronized->text());
+ text += QString("Wallet height: %1 \n").arg(ui->label_walletHeight->text());
+ text += QString("Daemon height: %1 \n").arg(ui->label_daemonHeight->text());
+ text += QString("Target height: %1 \n").arg(ui->label_targetHeight->text());
+ text += QString("Restore height: %1 \n").arg(ui->label_restoreHeight->text());
+ text += QString("Synchronized: %1 \n").arg(ui->label_synchronized->text());
- text += QString("Remote node: %1\n").arg(ui->label_remoteNode->text());
- text += QString("Wallet status: %1\n").arg(ui->label_walletStatus->text());
- text += QString("Tor status: %1\n").arg(ui->label_torStatus->text());
- text += QString("Websocket status: %1\n").arg(ui->label_websocketStatus->text());
+ text += QString("Remote node: %1 \n").arg(ui->label_remoteNode->text());
+ text += QString("Wallet status: %1 \n").arg(ui->label_walletStatus->text());
+ text += QString("Tor status: %1 \n").arg(ui->label_torStatus->text());
+ text += QString("Websocket status: %1 \n").arg(ui->label_websocketStatus->text());
- text += QString("Network type: %1\n").arg(ui->label_netType->text());
- text += QString("Seed type: %1\n").arg(ui->label_seedType->text());
- text += QString("View only: %1\n").arg(ui->label_viewOnly->text());
+ text += QString("Network type: %1 \n").arg(ui->label_netType->text());
+ text += QString("Seed type: %1 \n").arg(ui->label_seedType->text());
+ text += QString("View only: %1 \n").arg(ui->label_viewOnly->text());
- text += QString("Operating system: %1\n").arg(ui->label_OS->text());
- text += QString("Timestamp: %1\n").arg(ui->label_timestamp->text());
+ text += QString("Operating system: %1 \n").arg(ui->label_OS->text());
+ text += QString("Timestamp: %1 \n").arg(ui->label_timestamp->text());
Utils::copyToClipboard(text);
}
diff --git a/src/dialog/debuginfodialog.h b/src/dialog/debuginfodialog.h
index 2443df1..d9a6e4a 100644
--- a/src/dialog/debuginfodialog.h
+++ b/src/dialog/debuginfodialog.h
@@ -23,6 +23,10 @@ public:
private:
QString statusToString(Wallet::ConnectionStatus status);
void copyToClipboad();
+ void updateInfo();
+
+ QTimer m_updateTimer;
+ AppContext *m_ctx;
Ui::DebugInfoDialog *ui;
};
diff --git a/src/dialog/debuginfodialog.ui b/src/dialog/debuginfodialog.ui
index a613e6c..ecace2c 100644
--- a/src/dialog/debuginfodialog.ui
+++ b/src/dialog/debuginfodialog.ui
@@ -7,7 +7,7 @@
0
0
693
- 580
+ 612
@@ -311,6 +311,9 @@
TextLabel
+
+ Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse
+
diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp
index 31ab716..9e1abc6 100644
--- a/src/libwalletqt/Wallet.cpp
+++ b/src/libwalletqt/Wallet.cpp
@@ -47,6 +47,11 @@ Wallet::Wallet(QObject * parent)
{
}
+Wallet::ConnectionStatus Wallet::connectionStatus() const
+{
+ return m_connectionStatus;
+}
+
QString Wallet::getSeed() const
{
return QString::fromStdString(m_walletImpl->seed());
@@ -72,36 +77,6 @@ NetworkType::Type Wallet::nettype() const
return static_cast(m_walletImpl->nettype());
}
-
-void Wallet::updateConnectionStatusAsync()
-{
- m_scheduler.run([this] {
- if (m_connectionStatus == Wallet::ConnectionStatus_Disconnected)
- {
- setConnectionStatus(ConnectionStatus_Connecting);
- }
- ConnectionStatus newStatus = static_cast(m_walletImpl->connected());
- if (newStatus != m_connectionStatus || !m_initialized) {
- m_initialized = true;
- setConnectionStatus(newStatus);
- }
- // Release lock
- m_connectionStatusRunning = false;
- });
-}
-
-Wallet::ConnectionStatus Wallet::connected(bool forceCheck)
-{
- // cache connection status
- if (forceCheck || !m_initialized || (m_connectionStatusTime.elapsed() / 1000 > m_connectionStatusTtl && !m_connectionStatusRunning) || m_connectionStatusTime.elapsed() > 30000) {
- m_connectionStatusRunning = true;
- m_connectionStatusTime.restart();
- updateConnectionStatusAsync();
- }
-
- return m_connectionStatus;
-}
-
bool Wallet::disconnected() const
{
return m_disconnected;
@@ -120,6 +95,9 @@ void Wallet::refreshingSet(bool value)
}
}
+void Wallet::setConnectionTimeout(int timeout) {
+ m_connectionTimeout = timeout;
+}
void Wallet::setConnectionStatus(ConnectionStatus value)
{
@@ -232,16 +210,16 @@ bool Wallet::init(const QString &daemonAddress, bool trustedDaemon, quint64 uppe
{
QMutexLocker locker(&m_proxyMutex);
- if (!m_walletImpl->init(daemonAddress.toStdString(), upperTransactionLimit, m_daemonUsername.toStdString(), m_daemonPassword.toStdString(), false, false, proxyAddress.toStdString()))
+ if (!m_walletImpl->init(daemonAddress.toStdString(), upperTransactionLimit, m_daemonUsername.toStdString(), m_daemonPassword.toStdString(), m_useSSL, false, proxyAddress.toStdString()))
{
return false;
}
-
m_proxyAddress = proxyAddress;
}
emit proxyAddressChanged();
+ setTrustedDaemon(trustedDaemon);
setTrustedDaemon(trustedDaemon);
return true;
}
@@ -269,7 +247,7 @@ void Wallet::initAsync(
{
emit walletCreationHeightChanged();
qDebug() << "init async finished - starting refresh";
- connected(true);
+ refreshHeightAsync();
startRefresh();
}
});
@@ -313,6 +291,11 @@ void Wallet::setTrustedDaemon(bool arg)
m_walletImpl->setTrustedDaemon(arg);
}
+void Wallet::setUseSSL(bool ssl)
+{
+ m_useSSL = ssl;
+}
+
bool Wallet::viewOnly() const
{
return m_walletImpl->watchOnly();
@@ -425,6 +408,8 @@ void Wallet::refreshHeightAsync()
daemonHeightFuture.second.waitForFinished();
targetHeightFuture.second.waitForFinished();
+ setConnectionStatus(ConnectionStatus_Connected);
+
emit heightRefreshed(walletHeight, daemonHeight, targetHeight);
});
}
@@ -439,7 +424,8 @@ quint64 Wallet::daemonBlockChainHeight() const
// cache daemon blockchain height for some time (60 seconds by default)
if (m_daemonBlockChainHeight == 0
- || m_daemonBlockChainHeightTime.elapsed() / 1000 > m_daemonBlockChainHeightTtl) {
+ || m_daemonBlockChainHeightTime.elapsed() / 1000 > m_daemonBlockChainHeightTtl)
+ {
m_daemonBlockChainHeight = m_walletImpl->daemonBlockChainHeight();
m_daemonBlockChainHeightTime.restart();
}
@@ -449,7 +435,8 @@ quint64 Wallet::daemonBlockChainHeight() const
quint64 Wallet::daemonBlockChainTargetHeight() const
{
if (m_daemonBlockChainTargetHeight <= 1
- || m_daemonBlockChainTargetHeightTime.elapsed() / 1000 > m_daemonBlockChainTargetHeightTtl) {
+ || m_daemonBlockChainTargetHeightTime.elapsed() / 1000 > m_daemonBlockChainTargetHeightTtl)
+ {
m_daemonBlockChainTargetHeight = m_walletImpl->daemonBlockChainTargetHeight();
// Target height is set to 0 if daemon is synced.
@@ -501,6 +488,7 @@ bool Wallet::importTransaction(const QString& txid, const QVector& outp
void Wallet::startRefresh()
{
m_refreshEnabled = true;
+ m_refreshNow = true;
}
void Wallet::pauseRefresh()
@@ -1089,6 +1077,14 @@ void Wallet::onWalletPassphraseNeeded(bool on_device)
emit this->walletPassphraseNeeded(on_device);
}
+quint64 Wallet::getBytesReceived() const {
+ return m_walletImpl->getBytesReceived();
+}
+
+quint64 Wallet::getBytesSent() const {
+ return m_walletImpl->getBytesSent();
+}
+
void Wallet::onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort)
{
if (m_walletListener != nullptr)
@@ -1117,9 +1113,11 @@ Wallet::Wallet(Monero::Wallet *w, QObject *parent)
, m_subaddressAccount(nullptr)
, m_subaddressAccountModel(nullptr)
, m_coinsModel(nullptr)
+ , m_refreshNow(false)
, m_refreshEnabled(false)
, m_refreshing(false)
, m_scheduler(this)
+ , m_useSSL(true)
{
m_history = new TransactionHistory(m_walletImpl->history(), this);
m_addressBook = new AddressBook(m_walletImpl->addressBook(), this);
@@ -1191,8 +1189,9 @@ void Wallet::startRefreshThread()
{
const auto now = std::chrono::steady_clock::now();
const auto elapsed = now - last;
- if (elapsed >= refreshInterval)
+ if (elapsed >= refreshInterval || m_refreshNow)
{
+ m_refreshNow = false;
refresh(false);
last = std::chrono::steady_clock::now();
}
@@ -1205,4 +1204,12 @@ void Wallet::startRefreshThread()
{
throw std::runtime_error("failed to start auto refresh thread");
}
+}
+
+void Wallet::onRefreshed(bool success) {
+ if (success) {
+ setConnectionStatus(ConnectionStatus_Connected);
+ } else {
+ setConnectionStatus(ConnectionStatus_Disconnected);
+ }
}
\ No newline at end of file
diff --git a/src/libwalletqt/Wallet.h b/src/libwalletqt/Wallet.h
index a9c88ad..f44edc4 100644
--- a/src/libwalletqt/Wallet.h
+++ b/src/libwalletqt/Wallet.h
@@ -62,7 +62,7 @@ Q_OBJECT
Q_PROPERTY(QString seedLanguage READ getSeedLanguage)
Q_PROPERTY(Status status READ status)
Q_PROPERTY(NetworkType::Type nettype READ nettype)
-// Q_PROPERTY(ConnectionStatus connected READ connected)
+ Q_PROPERTY(ConnectionStatus connectionStatus READ connectionStatus)
Q_PROPERTY(quint32 currentSubaddressAccount READ currentSubaddressAccount NOTIFY currentSubaddressAccountChanged)
Q_PROPERTY(bool synchronized READ synchronized)
Q_PROPERTY(QString errorString READ errorString)
@@ -105,6 +105,9 @@ public:
Q_ENUM(ConnectionStatus)
+ //! return connection status
+ ConnectionStatus connectionStatus() const;
+
//! returns mnemonic seed
QString getSeed() const;
@@ -120,10 +123,6 @@ public:
//! returns network type of the wallet.
NetworkType::Type nettype() const;
- //! returns whether the wallet is connected, and version status
- Q_INVOKABLE ConnectionStatus connected(bool forceCheck = false);
- void updateConnectionStatusAsync();
-
//! returns true if wallet was ever synchronized
bool synchronized() const;
@@ -167,9 +166,15 @@ public:
//! connects to daemon
Q_INVOKABLE bool connectToDaemon();
- //! indicates id daemon is trusted
+ //! set connect to daemon timeout
+ Q_INVOKABLE void setConnectionTimeout(int timeout);
+
+ //! indicates if daemon is trusted
Q_INVOKABLE void setTrustedDaemon(bool arg);
+ //! indicates if ssl should be used to connect to daemon
+ Q_INVOKABLE void setUseSSL(bool ssl);
+
//! returns balance
Q_INVOKABLE quint64 balance() const;
Q_INVOKABLE quint64 balance(quint32 accountIndex) const;
@@ -394,6 +399,9 @@ public:
Q_INVOKABLE void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort=false);
virtual void onWalletPassphraseNeeded(bool on_device) override;
+ Q_INVOKABLE quint64 getBytesReceived() const;
+ Q_INVOKABLE quint64 getBytesSent() const;
+
// TODO: setListenter() when it implemented in API
signals:
// emitted on every event happened with wallet
@@ -402,7 +410,7 @@ signals:
// emitted when refresh process finished (could take a long time)
// signalling only after we
- void refreshed();
+ void refreshed(bool success);
void moneySpent(const QString &txId, quint64 amount);
void moneyReceived(const QString &txId, quint64 amount);
@@ -445,6 +453,7 @@ private:
bool disconnected() const;
bool refreshing() const;
void refreshingSet(bool value);
+ void onRefreshed(bool success);
void setConnectionStatus(ConnectionStatus value);
QString getProxyAddress() const;
@@ -489,10 +498,13 @@ private:
QString m_daemonPassword;
QString m_proxyAddress;
mutable QMutex m_proxyMutex;
+ std::atomic m_refreshNow;
std::atomic m_refreshEnabled;
std::atomic m_refreshing;
WalletListenerImpl *m_walletListener;
FutureScheduler m_scheduler;
+ int m_connectionTimeout = 30;
+ bool m_useSSL;
};
diff --git a/src/libwalletqt/WalletListenerImpl.cpp b/src/libwalletqt/WalletListenerImpl.cpp
index d857b9e..2a025c8 100644
--- a/src/libwalletqt/WalletListenerImpl.cpp
+++ b/src/libwalletqt/WalletListenerImpl.cpp
@@ -41,10 +41,11 @@ void WalletListenerImpl::updated()
}
// called when wallet refreshed by background thread or explicitly
-void WalletListenerImpl::refreshed()
+void WalletListenerImpl::refreshed(bool success)
{
qDebug() << __FUNCTION__;
- emit m_wallet->refreshed();
+ m_wallet->onRefreshed(success);
+ emit m_wallet->refreshed(success);
}
void WalletListenerImpl::onDeviceButtonRequest(uint64_t code)
diff --git a/src/libwalletqt/WalletListenerImpl.h b/src/libwalletqt/WalletListenerImpl.h
index 120ddf4..109ae59 100644
--- a/src/libwalletqt/WalletListenerImpl.h
+++ b/src/libwalletqt/WalletListenerImpl.h
@@ -25,7 +25,7 @@ public:
virtual void updated() override;
// called when wallet refreshed by background thread or explicitly
- virtual void refreshed() override;
+ virtual void refreshed(bool success) override;
virtual void onDeviceButtonRequest(uint64_t code) override;
diff --git a/src/libwalletqt/WalletManager.cpp b/src/libwalletqt/WalletManager.cpp
index d0caa6e..17cf353 100644
--- a/src/libwalletqt/WalletManager.cpp
+++ b/src/libwalletqt/WalletManager.cpp
@@ -26,7 +26,7 @@ public:
virtual void unconfirmedMoneyReceived(const std::string &txId, uint64_t amount) override { (void)txId; (void)amount; };
virtual void newBlock(uint64_t height) override { (void) height; };
virtual void updated() override {};
- virtual void refreshed() override {};
+ virtual void refreshed(bool success) override {};
virtual void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort) override
{
@@ -335,7 +335,7 @@ bool WalletManager::isMining() const
{
{
QMutexLocker locker(&m_mutex);
- if (m_currentWallet == nullptr || !m_currentWallet->connected())
+ if (m_currentWallet == nullptr || !m_currentWallet->connectionStatus())
{
return false;
}
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index f377784..5037b02 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -366,6 +366,8 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
this->initMain();
this->initWidgets();
this->initMenu();
+
+ connect(&m_updateBytes, &QTimer::timeout, this, &MainWindow::updateNetStats);
}
void MainWindow::initMain() {
@@ -669,6 +671,8 @@ void MainWindow::onWalletOpened() {
this->touchbarShowWallet();
this->updatePasswordIcon();
+
+ m_updateBytes.start(1000);
}
void MainWindow::onBalanceUpdated(double balance, double unlocked, const QString &balance_str, const QString &unlocked_str) {
@@ -688,6 +692,7 @@ void MainWindow::onBalanceUpdated(double balance, double unlocked, const QString
}
void MainWindow::onSynchronized() {
+ this->updateNetStats();
m_statusLabelStatus->setText("Synchronized");
this->onConnectionStatusChanged(Wallet::ConnectionStatus_Connected);
}
@@ -700,11 +705,12 @@ void MainWindow::onBlockchainSync(int height, int target) {
void MainWindow::onRefreshSync(int height, int target) {
QString heightText = QString("Wallet refresh: %1/%2").arg(height).arg(target);
m_statusLabelStatus->setText(heightText);
+ this->updateNetStats();
}
void MainWindow::onConnectionStatusChanged(int status)
{
- qDebug() << "Wallet connection status changed " << status;
+ qDebug() << "Wallet connection status changed " << Utils::QtEnumToString(static_cast(status));
// Update connection info in status bar.
@@ -746,7 +752,7 @@ void MainWindow::onCreateTransactionSuccess(PendingTransaction *tx, const QStrin
auto tx_err = tx->errorString();
qCritical() << tx_err;
- if(m_ctx->currentWallet->connected() == Wallet::ConnectionStatus_WrongVersion)
+ if (m_ctx->currentWallet->connectionStatus() == Wallet::ConnectionStatus_WrongVersion)
err = QString("%1 Wrong daemon version: %2").arg(err).arg(tx_err);
else
err = QString("%1 %2").arg(err).arg(tx_err);
@@ -826,6 +832,10 @@ void MainWindow::create_status_bar() {
m_statusLabelStatus->setTextInteractionFlags(Qt::TextSelectableByMouse);
this->statusBar()->addWidget(m_statusLabelStatus);
+ m_statusLabelNetStats = new QLabel("", this);
+ m_statusLabelNetStats->setTextInteractionFlags(Qt::TextSelectableByMouse);
+ this->statusBar()->addWidget(m_statusLabelNetStats);
+
m_statusLabelBalance = new QLabel("Balance: 0.00 XMR", this);
m_statusLabelBalance->setTextInteractionFlags(Qt::TextSelectableByMouse);
this->statusBar()->addPermanentWidget(m_statusLabelBalance);
@@ -864,7 +874,7 @@ void MainWindow::showSeedDialog() {
}
void MainWindow::showConnectionStatusDialog() {
- auto status = m_ctx->currentWallet->connected(true);
+ auto status = m_ctx->currentWallet->connectionStatus();
bool synchronized = m_ctx->currentWallet->synchronized();
QString statusMsg;
@@ -891,6 +901,9 @@ void MainWindow::showConnectionStatusDialog() {
statusMsg = "Unknown connection status (this should never happen).";
}
+ statusMsg += QString("\n\nTx: %1, Rx: %2").arg(Utils::formatBytes(m_ctx->currentWallet->getBytesSent()),
+ Utils::formatBytes(m_ctx->currentWallet->getBytesReceived()));
+
QMessageBox::information(this, "Connection Status", statusMsg);
}
@@ -1304,6 +1317,20 @@ void MainWindow::importTransaction() {
}
}
+void MainWindow::updateNetStats() {
+ if (!m_ctx->currentWallet) {
+ m_statusLabelNetStats->setText("");
+ return;
+ }
+
+ if (m_ctx->currentWallet->connectionStatus() == Wallet::ConnectionStatus_Connected && m_ctx->currentWallet->synchronized()) {
+ m_statusLabelNetStats->setText("");
+ return;
+ }
+
+ m_statusLabelNetStats->setText(QString("(D: %1)").arg(Utils::formatBytes(m_ctx->currentWallet->getBytesReceived())));
+}
+
MainWindow::~MainWindow() {
delete ui;
}
diff --git a/src/mainwindow.h b/src/mainwindow.h
index 7a2ae36..2bc86c6 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -163,6 +163,7 @@ private:
void touchbarShowWizard();
void touchbarShowWallet();
void updatePasswordIcon();
+ void updateNetStats();
WalletWizard *createWizard(WalletWizard::Page startPage);
@@ -192,6 +193,7 @@ private:
// lower status bar
QLabel *m_statusLabelBalance;
QLabel *m_statusLabelStatus;
+ QLabel *m_statusLabelNetStats;
StatusBarButton *m_statusBtnConnectionStatusIndicator;
StatusBarButton *m_statusBtnPassword;
StatusBarButton *m_statusBtnPreferences;
@@ -213,6 +215,8 @@ private:
QMap m_skins;
+ QTimer m_updateBytes;
+
private slots:
void menuToggleTabVisible(const QString &key);
};
diff --git a/src/model/NodeModel.cpp b/src/model/NodeModel.cpp
index 6b465b5..60ff93e 100644
--- a/src/model/NodeModel.cpp
+++ b/src/model/NodeModel.cpp
@@ -70,9 +70,9 @@ QVariant NodeModel::data(const QModelIndex &index, int role) const {
}
else if(role == Qt::BackgroundRole) {
if (node.isConnecting)
- return QBrush(QColor(186, 247, 255));
+ return QBrush(QColor("#A9DEF9"));
else if (node.isActive)
- return QBrush(QColor(158, 250, 158));
+ return QBrush(QColor("#78BC61"));
}
return QVariant();
}
diff --git a/src/settings.cpp b/src/settings.cpp
index 19c8b77..4e787fc 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -32,7 +32,7 @@ Settings::Settings(QWidget *parent) :
// nodes
ui->nodeWidget->setupUI(m_ctx);
connect(ui->nodeWidget, &NodeWidget::nodeSourceChanged, m_ctx->nodes, &Nodes::onNodeSourceChanged);
- connect(ui->nodeWidget, &NodeWidget::connectToNode, m_ctx->nodes, QOverload::of(&Nodes::connectToNode));
+ connect(ui->nodeWidget, &NodeWidget::connectToNode, m_ctx->nodes, QOverload::of(&Nodes::connectToNode));
// setup checkboxes
ui->checkBox_externalLink->setChecked(config()->get(Config::warnOnExternalLink).toBool());
diff --git a/src/ui/qdarkstyle/style.qss b/src/ui/qdarkstyle/style.qss
index 4605041..46a592f 100644
--- a/src/ui/qdarkstyle/style.qss
+++ b/src/ui/qdarkstyle/style.qss
@@ -1831,6 +1831,7 @@ QColumnView:selected {
color: #32414B;
}
+
QTreeView:hover,
QListView:hover,
QTableView:hover,
@@ -1838,6 +1839,7 @@ QColumnView:hover {
background-color: #19232D;
}
+
QTreeView::item:pressed,
QListView::item:pressed,
QTableView::item:pressed,
diff --git a/src/utils/nodes.cpp b/src/utils/nodes.cpp
index 0dd07d9..e071a55 100644
--- a/src/utils/nodes.cpp
+++ b/src/utils/nodes.cpp
@@ -4,11 +4,9 @@
#include
#include
#include
-#include
#include "nodes.h"
#include "utils/utils.h"
-#include "utils/networking.h"
#include "appcontext.h"
Nodes::Nodes(AppContext *ctx, QNetworkAccessManager *networkAccessManager, QObject *parent) :
@@ -19,23 +17,20 @@ Nodes::Nodes(AppContext *ctx, QNetworkAccessManager *networkAccessManager, QObje
modelWebsocket(new NodeModel(NodeSource::websocket, this)),
modelCustom(new NodeModel(NodeSource::custom, this)) {
this->loadConfig();
-
- connect(m_connectionTimer, &QTimer::timeout, this, &Nodes::onConnectionTimer);
}
void Nodes::loadConfig() {
- QString msg;
auto configNodes = config()->get(Config::nodes).toByteArray();
auto key = QString::number(m_ctx->networkType);
if (!Utils::validateJSON(configNodes)) {
m_configJson[key] = QJsonObject();
- qCritical() << "fixed malformed config key \"nodes\"";
+ qCritical() << "Fixed malformed config key \"nodes\"";
}
QJsonDocument doc = QJsonDocument::fromJson(configNodes);
m_configJson = doc.object();
- if(!m_configJson.contains(key))
+ if (!m_configJson.contains(key))
m_configJson[key] = QJsonObject();
auto obj = m_configJson.value(key).toObject();
@@ -46,7 +41,7 @@ void Nodes::loadConfig() {
// load custom nodes
auto nodes = obj.value("custom").toArray();
- foreach (const QJsonValue &value, nodes) {
+ for (auto value: nodes) {
auto customNode = FeatherNode(value.toString());
customNode.custom = true;
@@ -62,15 +57,15 @@ void Nodes::loadConfig() {
// load cached websocket nodes
auto ws = obj.value("ws").toArray();
- foreach (const QJsonValue &value, ws) {
+ for (auto value: ws) {
auto wsNode = FeatherNode(value.toString());
wsNode.custom = false;
wsNode.online = true; // assume online
- if(m_connection == wsNode) {
- if(m_connection.isActive)
+ if (m_connection == wsNode) {
+ if (m_connection.isActive)
wsNode.isActive = true;
- else if(m_connection.isConnecting)
+ else if (m_connection.isConnecting)
wsNode.isConnecting = true;
}
@@ -81,14 +76,12 @@ void Nodes::loadConfig() {
obj["source"] = NodeSource::websocket;
m_source = static_cast(obj.value("source").toInt());
- if(m_websocketNodes.count() > 0){
- msg = QString("Loaded %1 cached websocket nodes from config").arg(m_websocketNodes.count());
- activityLog.append(msg);
+ if (m_websocketNodes.count() > 0) {
+ qDebug() << QString("Loaded %1 cached websocket nodes from config").arg(m_websocketNodes.count());
}
- if(m_customNodes.count() > 0){
- msg = QString("Loaded %1 custom nodes from config").arg(m_customNodes.count());
- activityLog.append(msg);
+ if (m_customNodes.count() > 0) {
+ qDebug() << QString("Loaded %1 custom nodes from config").arg(m_customNodes.count());
}
m_configJson[key] = obj;
@@ -101,33 +94,30 @@ void Nodes::writeConfig() {
QString output(doc.toJson(QJsonDocument::Compact));
config()->set(Config::nodes, output);
- auto msg = QString("Saved node config.");
- activityLog.append(msg);
+ qDebug() << "Saved node config.";
}
void Nodes::connectToNode() {
// auto connect
- m_connectionAttempts.clear();
m_wsExhaustedWarningEmitted = false;
m_customExhaustedWarningEmitted = false;
- m_connectionTimer->start(2000);
- this->onConnectionTimer();
+ this->autoConnect();
}
-void Nodes::connectToNode(FeatherNode node) {
- if(node.address.isEmpty())
+void Nodes::connectToNode(const FeatherNode &node) {
+ if (node.address.isEmpty())
return;
emit updateStatus(QString("Connecting to %1").arg(node.address));
- auto msg = QString("Attempting to connect to %1 (%2)")
- .arg(node.address).arg(node.custom ? "custom" : "ws");
- qInfo() << msg;
- activityLog.append(msg);
+ qInfo() << QString("Attempting to connect to %1 (%2)").arg(node.address).arg(node.custom ? "custom" : "ws");
if (!node.username.isEmpty() && !node.password.isEmpty())
m_ctx->currentWallet->setDaemonLogin(node.username, node.password);
+
+ // Don't use SSL over Tor
+ m_ctx->currentWallet->setUseSSL(!node.tor);
+
m_ctx->currentWallet->initAsync(node.address, true, 0, false, false, 0);
- m_connectionAttemptTime = std::time(nullptr);
m_connection = node;
m_connection.isActive = false;
@@ -135,88 +125,55 @@ void Nodes::connectToNode(FeatherNode node) {
this->resetLocalState();
this->updateModels();
-
- m_connectionTimer->start(1000);
}
-void Nodes::onConnectionTimer() {
+void Nodes::autoConnect(bool forceReconnect) {
// this function is responsible for automatically connecting to a daemon.
- if (m_ctx->currentWallet == nullptr) {
- m_connectionTimer->stop();
+ if (m_ctx->currentWallet == nullptr || !m_enableAutoconnect) {
return;
}
- QString msg;
- Wallet::ConnectionStatus status = m_ctx->currentWallet->connected(true);
- NodeSource nodeSource = this->source();
- auto wsMode = (nodeSource == NodeSource::websocket);
+ Wallet::ConnectionStatus status = m_ctx->currentWallet->connectionStatus();
+ bool wsMode = (this->source() == NodeSource::websocket);
auto nodes = wsMode ? m_customNodes : m_websocketNodes;
if (wsMode && !m_wsNodesReceived && m_websocketNodes.count() == 0) {
// this situation should rarely occur due to the usage of the websocket node cache on startup.
- msg = QString("Feather is in websocket connection mode but was not able to receive any nodes (yet).");
- qInfo() << msg;
- activityLog.append(msg);
+ qInfo() << "Feather is in websocket connection mode but was not able to receive any nodes (yet).";
return;
}
- if (status == Wallet::ConnectionStatus::ConnectionStatus_Disconnected) {
+ if (status == Wallet::ConnectionStatus_Disconnected || forceReconnect) {
+ if (!m_connection.address.isEmpty() && !forceReconnect) {
+ m_recentFailures << m_connection.address;
+ }
+
// try a connect
auto node = this->pickEligibleNode();
this->connectToNode(node);
return;
- } else if (status == Wallet::ConnectionStatus::ConnectionStatus_Connecting){
- if (!m_connection.isConnecting) {
- // Weirdly enough, status == connecting directly after a wallet is opened.
- auto node = this->pickEligibleNode();
- this->connectToNode(node);
- return;
- }
-
- // determine timeout
- unsigned int nodeConnectionTimeout = 6;
- if(m_connection.tor)
- nodeConnectionTimeout = 25;
-
- auto connectionTimeout = static_cast(std::time(nullptr) - m_connectionAttemptTime);
- if(connectionTimeout < nodeConnectionTimeout)
- return; // timeout not reached yet
-
- msg = QString("Node connection attempt stale after %1 seconds, picking new node").arg(nodeConnectionTimeout);
- activityLog.append(msg);
- qInfo() << msg;
-
- auto newNode = this->pickEligibleNode();
- this->connectToNode(newNode);
- return;
- } else if(status == Wallet::ConnectionStatus::ConnectionStatus_Connected) {
- // wallet is connected to daemon successfully, poll status every 3 seconds
- if(!m_connection.isConnecting)
- return;
-
- msg = QString("Node connected to %1").arg(m_connection.address);
- qInfo() << msg;
- activityLog.append(msg);
+ }
+ else if (status == Wallet::ConnectionStatus_Connected && m_connection.isConnecting) {
+ qInfo() << QString("Node connected to %1").arg(m_connection.address);
// set current connection object
m_connection.isConnecting = false;
m_connection.isActive = true;
- this->resetLocalState();
- this->updateModels();
// reset node exhaustion state
- m_connectionAttempts.clear();
m_wsExhaustedWarningEmitted = false;
m_customExhaustedWarningEmitted = false;
- m_connectionTimer->setInterval(3000);
+ m_recentFailures.clear();
}
+
+ this->resetLocalState();
+ this->updateModels();
}
FeatherNode Nodes::pickEligibleNode() {
// Pick a node at random to connect to
auto rtn = FeatherNode();
- NodeSource nodeSource = this->source();
- auto wsMode = nodeSource == NodeSource::websocket;
+ auto wsMode = (this->source() == NodeSource::websocket);
auto nodes = wsMode ? m_websocketNodes : m_customNodes;
if (nodes.count() == 0) {
@@ -224,51 +181,23 @@ FeatherNode Nodes::pickEligibleNode() {
return rtn;
}
- QVector heights;
+ QVector node_indeces;
+ int i = 0;
for (const auto &node: nodes) {
- heights.push_back(node.height);
+ node_indeces.push_back(i);
+ i++;
}
+ unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
+ std::shuffle(node_indeces.begin(), node_indeces.end(), std::default_random_engine(seed));
- std::sort(heights.begin(), heights.end());
+ // Pick random eligible node
+ int mode_height = this->modeHeight(nodes);
+ for (int index : node_indeces) {
+ const FeatherNode &node = nodes.at(index);
- // Calculate mode of node heights
- int max_count = 1, mode_height = heights[0], count = 1;
- for (int i = 1; i < heights.count(); i++) {
- if (heights[i] == 0) { // Don't consider 0 height nodes
- continue;
- }
-
- if (heights[i] == heights[i - 1])
- count++;
- else {
- if (count > max_count) {
- max_count = count;
- mode_height = heights[i - 1];
- }
- count = 1;
- }
- }
- if (count > max_count)
- {
- max_count = count;
- mode_height = heights[heights.count() - 1];
- }
-
- while(true) {
- // keep track of nodes we have previously tried to connect to
- if (m_connectionAttempts.count() == nodes.count()) {
- this->exhausted();
- m_connectionTimer->stop();
- return rtn;
- }
-
- int random = QRandomGenerator::global()->bounded(nodes.count());
- FeatherNode node = nodes.at(random);
- if (m_connectionAttempts.contains(node.full))
- continue;
- m_connectionAttempts.append(node.full);
-
- if (wsMode) {
+ // This may fail to detect bad nodes if cached nodes are used
+ // Todo: wait on websocket before connecting, only use cache if websocket is unavailable
+ if (wsMode && m_wsNodesReceived) {
// Ignore offline nodes
if (!node.online)
continue;
@@ -282,19 +211,28 @@ FeatherNode Nodes::pickEligibleNode() {
continue;
}
+ // Don't connect to nodes that failed to connect recently
+ if (m_recentFailures.contains(node.address)) {
+ continue;
+ }
+
return node;
}
+
+ // All nodes tried, and none eligible
+ this->exhausted();
+ return rtn;
}
void Nodes::onWSNodesReceived(const QList> &nodes) {
m_websocketNodes.clear();
m_wsNodesReceived = true;
- for(auto &node: nodes) {
- if(m_connection == *node) {
- if(m_connection.isActive)
+ for (auto &node: nodes) {
+ if (m_connection == *node) {
+ if (m_connection.isActive)
node->isActive = true;
- else if(m_connection.isConnecting)
+ else if (m_connection.isConnecting)
node->isConnecting = true;
}
m_websocketNodes.push_back(*node);
@@ -304,7 +242,7 @@ void Nodes::onWSNodesReceived(const QList> &nodes) {
auto key = QString::number(m_ctx->networkType);
auto obj = m_configJson.value(key).toObject();
auto ws = QJsonArray();
- for(auto const &node: m_websocketNodes)
+ for (auto const &node: m_websocketNodes)
ws.push_back(node.address);
obj["ws"] = ws;
@@ -315,7 +253,10 @@ void Nodes::onWSNodesReceived(const QList> &nodes) {
}
void Nodes::onNodeSourceChanged(NodeSource nodeSource) {
- if(nodeSource == this->source()) return;
+ if (nodeSource == this->source())
+ return;
+ m_source = nodeSource;
+
auto key = QString::number(m_ctx->networkType);
auto obj = m_configJson.value(key).toObject();
obj["source"] = nodeSource;
@@ -324,16 +265,18 @@ void Nodes::onNodeSourceChanged(NodeSource nodeSource) {
this->writeConfig();
this->resetLocalState();
this->updateModels();
+
+ this->autoConnect(true);
}
-void Nodes::setCustomNodes(QList nodes) {
+void Nodes::setCustomNodes(const QList &nodes) {
m_customNodes.clear();
auto key = QString::number(m_ctx->networkType);
auto obj = m_configJson.value(key).toObject();
QStringList nodesList;
- for(auto const &node: nodes) {
- if(nodesList.contains(node.full)) continue;
+ for (auto const &node: nodes) {
+ if (nodesList.contains(node.full)) continue;
nodesList.append(node.full);
m_customNodes.append(node);
}
@@ -352,56 +295,47 @@ void Nodes::updateModels() {
}
void Nodes::resetLocalState() {
- QList*> models = {&m_customNodes, &m_websocketNodes};
+ auto resetState = [this](QList *model){
+ for (auto&& node: *model) {
+ node.isConnecting = false;
+ node.isActive = false;
- for(QList *model: models) {
- for (FeatherNode &_node: *model) {
- _node.isConnecting = false;
- _node.isActive = false;
-
- if (_node == m_connection) {
- _node.isActive = m_connection.isActive;
- _node.isConnecting = m_connection.isConnecting;
+ if (node == m_connection) {
+ node.isActive = m_connection.isActive;
+ node.isConnecting = m_connection.isConnecting;
}
}
- }
+ };
+
+ resetState(&m_customNodes);
+ resetState(&m_websocketNodes);
}
void Nodes::exhausted() {
- NodeSource nodeSource = this->source();
- auto wsMode = nodeSource == NodeSource::websocket;
- if(wsMode)
+ bool wsMode = (this->source() == NodeSource::websocket);
+
+ if (wsMode)
this->WSNodeExhaustedWarning();
else
this->nodeExhaustedWarning();
}
void Nodes::nodeExhaustedWarning(){
- if(m_customExhaustedWarningEmitted) return;
+ if (m_customExhaustedWarningEmitted)
+ return;
+
emit nodeExhausted();
-
- auto msg = QString("Could not find an eligible custom node to connect to.");
- qWarning() << msg;
- activityLog.append(msg);
-
+ qWarning() << "Could not find an eligible custom node to connect to.";
m_customExhaustedWarningEmitted = true;
- this->m_connectionTimer->stop();
}
void Nodes::WSNodeExhaustedWarning() {
- if(m_wsExhaustedWarningEmitted) return;
+ if (m_wsExhaustedWarningEmitted)
+ return;
+
emit WSNodeExhausted();
-
- auto msg = QString("Could not find an eligible websocket node to connect to.");
- qWarning() << msg;
- activityLog.append(msg);
-
+ qWarning() << "Could not find an eligible websocket node to connect to.";
m_wsExhaustedWarningEmitted = true;
- this->m_connectionTimer->stop();
-}
-
-void Nodes::onWalletClosing() {
- m_connectionTimer->stop();
}
QList Nodes::customNodes() {
@@ -412,10 +346,38 @@ FeatherNode Nodes::connection() {
return m_connection;
}
-void Nodes::stopTimer(){
- m_connectionTimer->stop();
-}
-
NodeSource Nodes::source() {
return m_source;
}
+
+int Nodes::modeHeight(const QList &nodes) {
+ QVector heights;
+ for (const auto &node: nodes) {
+ heights.push_back(node.height);
+ }
+
+ std::sort(heights.begin(), heights.end());
+
+ int max_count = 1, mode_height = heights[0], count = 1;
+ for (int i = 1; i < heights.count(); i++) {
+ if (heights[i] == 0) { // Don't consider 0 height nodes
+ continue;
+ }
+
+ if (heights[i] == heights[i - 1])
+ count++;
+ else {
+ if (count > max_count) {
+ max_count = count;
+ mode_height = heights[i - 1];
+ }
+ count = 1;
+ }
+ }
+ if (count > max_count)
+ {
+ mode_height = heights[heights.count() - 1];
+ }
+
+ return mode_height;
+}
\ No newline at end of file
diff --git a/src/utils/nodes.h b/src/utils/nodes.h
index 37ba854..81c9627 100644
--- a/src/utils/nodes.h
+++ b/src/utils/nodes.h
@@ -86,7 +86,6 @@ public:
explicit Nodes(AppContext *ctx, QNetworkAccessManager *networkAccessManager, QObject *parent = nullptr);
void loadConfig();
void writeConfig();
- void stopTimer();
NodeSource source();
FeatherNode connection();
@@ -95,18 +94,13 @@ public:
NodeModel *modelWebsocket;
NodeModel *modelCustom;
- QStringList activityLog;
-
public slots:
- void onWalletClosing();
void connectToNode();
- void connectToNode(FeatherNode node);
+ void connectToNode(const FeatherNode &node);
void onWSNodesReceived(const QList>& nodes);
void onNodeSourceChanged(NodeSource nodeSource);
- void setCustomNodes(QList nodes);
-
-private slots:
- void onConnectionTimer();
+ void setCustomNodes(const QList& nodes);
+ void autoConnect(bool forceReconnect = false);
signals:
void WSNodeExhausted();
@@ -119,18 +113,17 @@ private:
QNetworkAccessManager *m_networkAccessManager = nullptr;
QJsonObject m_configJson;
+ QStringList m_recentFailures;
+
QList m_customNodes;
QList m_websocketNodes;
FeatherNode m_connection; // current active connection, if any
- QTimer *m_connectionTimer = new QTimer(this);
- time_t m_connectionAttemptTime = 0;
- QStringList m_connectionAttempts;
bool m_wsNodesReceived = false;
-
bool m_wsExhaustedWarningEmitted = true;
bool m_customExhaustedWarningEmitted = true;
+ bool m_enableAutoconnect = true;
FeatherNode pickEligibleNode();
@@ -139,6 +132,7 @@ private:
void exhausted();
void WSNodeExhaustedWarning();
void nodeExhaustedWarning();
+ int modeHeight(const QList &nodes);
};
#endif //FEATHER_NODES_H
diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp
index e7d52a4..f99737f 100644
--- a/src/utils/utils.cpp
+++ b/src/utils/utils.cpp
@@ -541,9 +541,8 @@ QFont Utils::relativeFont(int delta) {
double Utils::roundSignificant(double N, double n)
{
int h;
- double l, a, b, c, d, e, i, j, m, f, g;
+ double b, d, e, i, j, m, f;
b = N;
- c = floor(N);
for (i = 0; b >= 1; ++i)
b = b / 10;
@@ -564,3 +563,22 @@ double Utils::roundSignificant(double N, double n)
j = j / m;
return j;
}
+
+QString Utils::formatBytes(quint64 bytes)
+{
+ QVector sizes = { "B", "KB", "MB", "GB", "TB" };
+
+ int i;
+ double _data;
+ for (i = 0; i < sizes.count() && bytes >= 1000; i++, bytes /= 1000)
+ _data = bytes / 1000.0;
+
+ if (_data < 0)
+ _data = 0;
+
+ // unrealistic
+ if (_data > 1000)
+ _data = 0;
+
+ return QString("%1 %2").arg(QString::number(_data, 'f', 1), sizes[i]);
+}
\ No newline at end of file
diff --git a/src/utils/utils.h b/src/utils/utils.h
index 1595de6..fa0a92a 100644
--- a/src/utils/utils.h
+++ b/src/utils/utils.h
@@ -95,6 +95,8 @@ public:
static bool pixmapWrite(const QString &path, const QPixmap &pixmap);
static QFont relativeFont(int delta);
static double roundSignificant(double N, double n);
+ static QString formatBytes(quint64 bytes);
+
static QStringList randomHTTPAgents;
template