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