mirror of
https://github.com/feather-wallet/feather.git
synced 2024-11-17 17:57:39 +00:00
Merge pull request 'Libwalletqt: integrate changes from upstream' (#63) from tobtoht/feather:syncing into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/63
This commit is contained in:
commit
63e2ba799d
4 changed files with 226 additions and 114 deletions
|
@ -2,6 +2,11 @@
|
|||
// Copyright (c) 2014-2020, The Monero Project.
|
||||
|
||||
#include "Wallet.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
|
||||
#include "PendingTransaction.h"
|
||||
#include "UnsignedTransaction.h"
|
||||
#include "TransactionHistory.h"
|
||||
|
@ -27,6 +32,8 @@
|
|||
#include <QVector>
|
||||
#include <QMutexLocker>
|
||||
|
||||
#include "utils/ScopeGuard.h"
|
||||
|
||||
namespace {
|
||||
static const int DAEMON_BLOCKCHAIN_HEIGHT_CACHE_TTL_SECONDS = 5;
|
||||
static const int DAEMON_BLOCKCHAIN_TARGET_HEIGHT_CACHE_TTL_SECONDS = 30;
|
||||
|
@ -100,6 +107,19 @@ bool Wallet::disconnected() const
|
|||
return m_disconnected;
|
||||
}
|
||||
|
||||
bool Wallet::refreshing() const
|
||||
{
|
||||
return m_refreshing;
|
||||
}
|
||||
|
||||
void Wallet::refreshingSet(bool value)
|
||||
{
|
||||
if (m_refreshing.exchange(value) != value)
|
||||
{
|
||||
emit refreshingChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void Wallet::setConnectionStatus(ConnectionStatus value)
|
||||
{
|
||||
if (m_connectionStatus == value)
|
||||
|
@ -120,6 +140,29 @@ void Wallet::setConnectionStatus(ConnectionStatus value)
|
|||
}
|
||||
}
|
||||
|
||||
QString Wallet::getProxyAddress() const
|
||||
{
|
||||
QMutexLocker locker(&m_proxyMutex);
|
||||
return m_proxyAddress;
|
||||
}
|
||||
|
||||
void Wallet::setProxyAddress(QString address)
|
||||
{
|
||||
m_scheduler.run([this, address] {
|
||||
{
|
||||
QMutexLocker locker(&m_proxyMutex);
|
||||
|
||||
if (!m_walletImpl->setProxy(address.toStdString()))
|
||||
{
|
||||
qCritical() << "failed to set proxy" << address;
|
||||
}
|
||||
|
||||
m_proxyAddress = address;
|
||||
}
|
||||
emit proxyAddressChanged();
|
||||
});
|
||||
}
|
||||
|
||||
bool Wallet::synchronized() const
|
||||
{
|
||||
return m_walletImpl->synchronized();
|
||||
|
@ -145,20 +188,18 @@ QString Wallet::path() const
|
|||
return QDir::toNativeSeparators(QString::fromStdString(m_walletImpl->path()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
//void Wallet::storeAsync(const QVariant &callback, const QString &path /* = "" */)
|
||||
//void Wallet::storeAsync(const QJSValue &callback, const QString &path /* = "" */)
|
||||
//{
|
||||
// const auto future = m_scheduler.run(
|
||||
// [this, path] {
|
||||
// QMutexLocker locker(&m_storeMutex);
|
||||
|
||||
// return QVariantList({m_walletImpl->store(path.toStdString())});
|
||||
// QMutexLocker locker(&m_asyncMutex);
|
||||
//
|
||||
// return QJSValueList({m_walletImpl->store(path.toStdString())});
|
||||
// },
|
||||
// callback);
|
||||
// if (!future.first)
|
||||
// {
|
||||
// QVariant(callback).call(QVariantList({false}));
|
||||
// QJSValue(callback).call(QJSValueList({false}));
|
||||
// }
|
||||
//}
|
||||
|
||||
|
@ -167,7 +208,7 @@ void Wallet::store(const QString &path)
|
|||
m_walletImpl->store(path.toStdString());
|
||||
}
|
||||
|
||||
bool Wallet::init(const QString &daemonAddress, bool trustedDaemon, quint64 upperTransactionLimit, bool isRecovering, bool isRecoveringFromDevice, quint64 restoreHeight)
|
||||
bool Wallet::init(const QString &daemonAddress, bool trustedDaemon, quint64 upperTransactionLimit, bool isRecovering, bool isRecoveringFromDevice, quint64 restoreHeight, const QString& proxyAddress)
|
||||
{
|
||||
qDebug() << "init non async";
|
||||
if (isRecovering){
|
||||
|
@ -181,7 +222,20 @@ bool Wallet::init(const QString &daemonAddress, bool trustedDaemon, quint64 uppe
|
|||
if (isRecovering || isRecoveringFromDevice) {
|
||||
m_walletImpl->setRefreshFromBlockHeight(restoreHeight);
|
||||
}
|
||||
m_walletImpl->init(daemonAddress.toStdString(), upperTransactionLimit, m_daemonUsername.toStdString(), m_daemonPassword.toStdString());
|
||||
|
||||
{
|
||||
QMutexLocker locker(&m_proxyMutex);
|
||||
|
||||
if (!m_walletImpl->init(daemonAddress.toStdString(), upperTransactionLimit, m_daemonUsername.toStdString(), m_daemonPassword.toStdString(), false, false, proxyAddress.toStdString()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
m_proxyAddress = proxyAddress;
|
||||
}
|
||||
emit proxyAddressChanged();
|
||||
|
||||
setTrustedDaemon(trustedDaemon);
|
||||
return true;
|
||||
}
|
||||
|
@ -193,17 +247,24 @@ void Wallet::setDaemonLogin(const QString &daemonUsername, const QString &daemon
|
|||
m_daemonPassword = daemonPassword;
|
||||
}
|
||||
|
||||
void Wallet::initAsync(const QString &daemonAddress, bool trustedDaemon, quint64 upperTransactionLimit, bool isRecovering, bool isRecoveringFromDevice, quint64 restoreHeight)
|
||||
void Wallet::initAsync(
|
||||
const QString &daemonAddress,
|
||||
bool trustedDaemon /* = false */,
|
||||
quint64 upperTransactionLimit /* = 0 */,
|
||||
bool isRecovering /* = false */,
|
||||
bool isRecoveringFromDevice /* = false */,
|
||||
quint64 restoreHeight /* = 0 */,
|
||||
const QString &proxyAddress /* = "" */)
|
||||
{
|
||||
qDebug() << "initAsync: " + daemonAddress;
|
||||
const auto future = m_scheduler.run([this, daemonAddress, trustedDaemon, upperTransactionLimit, isRecovering, isRecoveringFromDevice, restoreHeight] {
|
||||
bool success = init(daemonAddress, trustedDaemon, upperTransactionLimit, isRecovering, isRecoveringFromDevice, restoreHeight);
|
||||
const auto future = m_scheduler.run([this, daemonAddress, trustedDaemon, upperTransactionLimit, isRecovering, isRecoveringFromDevice, restoreHeight, proxyAddress] {
|
||||
bool success = init(daemonAddress, trustedDaemon, upperTransactionLimit, isRecovering, isRecoveringFromDevice, restoreHeight, proxyAddress);
|
||||
if (success)
|
||||
{
|
||||
emit walletCreationHeightChanged();
|
||||
qDebug() << "init async finished - starting refresh";
|
||||
connected(true);
|
||||
m_walletImpl->startRefresh();
|
||||
startRefresh();
|
||||
}
|
||||
});
|
||||
if (future.first)
|
||||
|
@ -406,42 +467,37 @@ bool Wallet::importKeyImages(const QString& path)
|
|||
return m_walletImpl->importKeyImages(path.toStdString());
|
||||
}
|
||||
|
||||
bool Wallet::refresh()
|
||||
bool Wallet::refresh(bool historyAndSubaddresses /* = true */)
|
||||
{
|
||||
refreshingSet(true);
|
||||
const auto cleanup = sg::make_scope_guard([this]() noexcept {
|
||||
refreshingSet(false);
|
||||
});
|
||||
|
||||
{
|
||||
QMutexLocker locker(&m_asyncMutex);
|
||||
|
||||
bool result = m_walletImpl->refresh();
|
||||
if (historyAndSubaddresses)
|
||||
{
|
||||
m_history->refresh(currentSubaddressAccount());
|
||||
m_subaddress->refresh(currentSubaddressAccount());
|
||||
m_coins->refresh(currentSubaddressAccount());
|
||||
m_subaddressAccount->getAll();
|
||||
}
|
||||
if (result)
|
||||
emit updated();
|
||||
return result;
|
||||
}
|
||||
|
||||
void Wallet::refreshAsync()
|
||||
{
|
||||
qDebug() << "refresh async";
|
||||
m_walletImpl->refreshAsync();
|
||||
}
|
||||
|
||||
void Wallet::setAutoRefreshInterval(int seconds)
|
||||
void Wallet::startRefresh()
|
||||
{
|
||||
m_walletImpl->setAutoRefreshInterval(seconds);
|
||||
m_refreshEnabled = true;
|
||||
}
|
||||
|
||||
int Wallet::autoRefreshInterval() const
|
||||
void Wallet::pauseRefresh()
|
||||
{
|
||||
return m_walletImpl->autoRefreshInterval();
|
||||
}
|
||||
|
||||
void Wallet::startRefresh() const
|
||||
{
|
||||
m_walletImpl->startRefresh();
|
||||
}
|
||||
|
||||
void Wallet::pauseRefresh() const
|
||||
{
|
||||
m_walletImpl->pauseRefresh();
|
||||
m_refreshEnabled = false;
|
||||
}
|
||||
|
||||
PendingTransaction *Wallet::createTransaction(const QString &dst_addr, const QString &payment_id,
|
||||
|
@ -556,7 +612,6 @@ void Wallet::disposeTransaction(UnsignedTransaction *t)
|
|||
delete t;
|
||||
}
|
||||
|
||||
// @ TODO: QJSValue could probably be QVARIANT
|
||||
//void Wallet::estimateTransactionFeeAsync(const QString &destination,
|
||||
// quint64 amount,
|
||||
// PendingTransaction::Priority priority,
|
||||
|
@ -831,7 +886,6 @@ bool Wallet::verifySignedMessage(const QString &message, const QString &address,
|
|||
return m_walletImpl->verifySignedMessage(message.toStdString(), address.toStdString(), signature.toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
bool Wallet::parse_uri(const QString &uri, QString &address, QString &payment_id, uint64_t &amount, QString &tx_description, QString &recipient_name, QVector<QString> &unknown_parameters, QString &error)
|
||||
{
|
||||
std::string s_address, s_payment_id, s_tx_description, s_recipient_name, s_error;
|
||||
|
@ -852,6 +906,8 @@ bool Wallet::parse_uri(const QString &uri, QString &address, QString &payment_id
|
|||
|
||||
bool Wallet::rescanSpent()
|
||||
{
|
||||
QMutexLocker locker(&m_asyncMutex);
|
||||
|
||||
return m_walletImpl->rescanSpent();
|
||||
}
|
||||
|
||||
|
@ -1020,6 +1076,8 @@ Wallet::Wallet(Monero::Wallet *w, QObject *parent)
|
|||
, m_subaddressAccount(nullptr)
|
||||
, m_subaddressAccountModel(nullptr)
|
||||
, m_coinsModel(nullptr)
|
||||
, m_refreshEnabled(false)
|
||||
, m_refreshing(false)
|
||||
, m_scheduler(this)
|
||||
{
|
||||
m_history = new TransactionHistory(m_walletImpl->history(), this);
|
||||
|
@ -1038,11 +1096,13 @@ Wallet::Wallet(Monero::Wallet *w, QObject *parent)
|
|||
m_connectionStatusRunning = false;
|
||||
m_daemonUsername = "";
|
||||
m_daemonPassword = "";
|
||||
|
||||
startRefreshThread();
|
||||
}
|
||||
|
||||
Wallet::~Wallet()
|
||||
{
|
||||
qDebug() << "~Wallet: Closing wallet";
|
||||
qDebug("~Wallet: Closing wallet");
|
||||
|
||||
m_scheduler.shutdownWaitForFinished();
|
||||
|
||||
|
@ -1072,3 +1132,32 @@ Wallet::~Wallet()
|
|||
m_walletListener = NULL;
|
||||
qDebug("m_walletImpl deleted");
|
||||
}
|
||||
|
||||
void Wallet::startRefreshThread()
|
||||
{
|
||||
const auto future = m_scheduler.run([this] {
|
||||
static constexpr const size_t refreshIntervalSec = 10;
|
||||
static constexpr const size_t intervalResolutionMs = 100;
|
||||
|
||||
auto last = std::chrono::steady_clock::now();
|
||||
while (!m_scheduler.stopping())
|
||||
{
|
||||
if (m_refreshEnabled)
|
||||
{
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(now - last).count();
|
||||
if (elapsed >= refreshIntervalSec)
|
||||
{
|
||||
refresh(false);
|
||||
last = std::chrono::steady_clock::now();
|
||||
}
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(intervalResolutionMs));
|
||||
}
|
||||
});
|
||||
if (!future.first)
|
||||
{
|
||||
throw std::runtime_error("failed to start auto refresh thread");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include <QObject>
|
||||
#include <QMutex>
|
||||
#include <QList>
|
||||
//#include <QJSValue>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
|
||||
#include "wallet/api/wallet2_api.h" // we need to have an access to the Monero::Wallet::Status enum here;
|
||||
|
@ -27,7 +26,6 @@ struct Wallet; // forward declaration
|
|||
class TransactionHistory;
|
||||
class TransactionHistoryModel;
|
||||
class TransactionHistoryProxyModel;
|
||||
class TransactionHistorySortFilterModel;
|
||||
class AddressBook;
|
||||
class AddressBookModel;
|
||||
class Subaddress;
|
||||
|
@ -52,11 +50,12 @@ class Wallet : public QObject, public PassprasePrompter
|
|||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool disconnected READ disconnected NOTIFY disconnectedChanged)
|
||||
Q_PROPERTY(bool refreshing READ refreshing NOTIFY refreshingChanged)
|
||||
Q_PROPERTY(QString seed READ getSeed)
|
||||
Q_PROPERTY(QString seedLanguage READ getSeedLanguage)
|
||||
Q_PROPERTY(Status status READ status)
|
||||
Q_PROPERTY(NetworkType::Type nettype READ nettype)
|
||||
Q_PROPERTY(ConnectionStatus connected READ connected)
|
||||
// Q_PROPERTY(ConnectionStatus connected READ connected)
|
||||
Q_PROPERTY(quint32 currentSubaddressAccount READ currentSubaddressAccount NOTIFY currentSubaddressAccountChanged)
|
||||
Q_PROPERTY(bool synchronized READ synchronized)
|
||||
Q_PROPERTY(QString errorString READ errorString)
|
||||
|
@ -76,6 +75,7 @@ class Wallet : public QObject, public PassprasePrompter
|
|||
Q_PROPERTY(QString secretSpendKey READ getSecretSpendKey)
|
||||
Q_PROPERTY(QString publicSpendKey READ getPublicSpendKey)
|
||||
Q_PROPERTY(QString daemonLogPath READ getDaemonLogPath CONSTANT)
|
||||
Q_PROPERTY(QString proxyAddress READ getProxyAddress WRITE setProxyAddress NOTIFY proxyAddressChanged)
|
||||
Q_PROPERTY(quint64 walletCreationHeight READ getWalletCreationHeight WRITE setWalletCreationHeight NOTIFY walletCreationHeightChanged)
|
||||
|
||||
public:
|
||||
|
@ -120,6 +120,7 @@ public:
|
|||
//! returns true if wallet was ever synchronized
|
||||
bool synchronized() const;
|
||||
|
||||
|
||||
//! returns last operation's error message
|
||||
QString errorString() const;
|
||||
|
||||
|
@ -138,7 +139,14 @@ public:
|
|||
// Q_INVOKABLE void storeAsync(const QJSValue &callback, const QString &path = "");
|
||||
|
||||
//! initializes wallet asynchronously
|
||||
Q_INVOKABLE void initAsync(const QString &daemonAddress, bool trustedDaemon = false, quint64 upperTransactionLimit = 0, bool isRecovering = false, bool isRecoveringFromDevice = false, quint64 restoreHeight = 0);
|
||||
Q_INVOKABLE void initAsync(
|
||||
const QString &daemonAddress,
|
||||
bool trustedDaemon = false,
|
||||
quint64 upperTransactionLimit = 0,
|
||||
bool isRecovering = false,
|
||||
bool isRecoveringFromDevice = false,
|
||||
quint64 restoreHeight = 0,
|
||||
const QString &proxyAddress = "");
|
||||
|
||||
// Set daemon rpc user/pass
|
||||
Q_INVOKABLE void setDaemonLogin(const QString &daemonUsername = "", const QString &daemonPassword = "");
|
||||
|
@ -188,20 +196,11 @@ public:
|
|||
Q_INVOKABLE bool importKeyImages(const QString& path);
|
||||
|
||||
//! refreshes the wallet
|
||||
Q_INVOKABLE bool refresh();
|
||||
|
||||
//! refreshes the wallet asynchronously
|
||||
Q_INVOKABLE void refreshAsync();
|
||||
|
||||
//! setup auto-refresh interval in seconds
|
||||
Q_INVOKABLE void setAutoRefreshInterval(int seconds);
|
||||
|
||||
//! return auto-refresh interval in seconds
|
||||
Q_INVOKABLE int autoRefreshInterval() const;
|
||||
Q_INVOKABLE bool refresh(bool historyAndSubaddresses = true);
|
||||
|
||||
// pause/resume refresh
|
||||
Q_INVOKABLE void startRefresh() const;
|
||||
Q_INVOKABLE void pauseRefresh() const;
|
||||
Q_INVOKABLE void startRefresh();
|
||||
Q_INVOKABLE void pauseRefresh();
|
||||
|
||||
//! returns current wallet's block height
|
||||
//! (can be less than daemon's blockchain height when wallet sync in progress)
|
||||
|
@ -402,6 +401,8 @@ signals:
|
|||
void connectionStatusChanged(int status) const;
|
||||
void currentSubaddressAccountChanged() const;
|
||||
void disconnectedChanged() const;
|
||||
void proxyAddressChanged() const;
|
||||
void refreshingChanged() const;
|
||||
|
||||
private:
|
||||
Wallet(QObject * parent = nullptr);
|
||||
|
@ -409,10 +410,22 @@ private:
|
|||
~Wallet();
|
||||
|
||||
//! initializes wallet
|
||||
bool init(const QString &daemonAddress, bool trustedDaemon, quint64 upperTransactionLimit, bool isRecovering, bool isRecoveringFromDevice, quint64 restoreHeight);
|
||||
bool init(
|
||||
const QString &daemonAddress,
|
||||
bool trustedDaemon,
|
||||
quint64 upperTransactionLimit,
|
||||
bool isRecovering,
|
||||
bool isRecoveringFromDevice,
|
||||
quint64 restoreHeight,
|
||||
const QString& proxyAddress);
|
||||
|
||||
bool disconnected() const;
|
||||
bool refreshing() const;
|
||||
void refreshingSet(bool value);
|
||||
void setConnectionStatus(ConnectionStatus value);
|
||||
QString getProxyAddress() const;
|
||||
void setProxyAddress(QString address);
|
||||
void startRefreshThread();
|
||||
|
||||
private:
|
||||
friend class WalletManager;
|
||||
|
@ -445,13 +458,17 @@ private:
|
|||
mutable SubaddressAccountModel * m_subaddressAccountModel;
|
||||
Coins * m_coins;
|
||||
mutable CoinsModel * m_coinsModel;
|
||||
QMutex m_asyncMutex;
|
||||
QMutex m_connectionStatusMutex;
|
||||
bool m_connectionStatusRunning;
|
||||
QString m_daemonUsername;
|
||||
QString m_daemonPassword;
|
||||
QString m_proxyAddress;
|
||||
mutable QMutex m_proxyMutex;
|
||||
std::atomic<bool> m_refreshEnabled;
|
||||
std::atomic<bool> m_refreshing;
|
||||
WalletListenerImpl *m_walletListener;
|
||||
FutureScheduler m_scheduler;
|
||||
QMutex m_storeMutex;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -68,6 +68,11 @@ QPair<bool, QFuture<void>> FutureScheduler::run(std::function<void()> function)
|
|||
// });
|
||||
//}
|
||||
|
||||
bool FutureScheduler::stopping() const noexcept
|
||||
{
|
||||
return Stopping;
|
||||
}
|
||||
|
||||
bool FutureScheduler::add() noexcept
|
||||
{
|
||||
QMutexLocker locker(&Mutex);
|
||||
|
|
|
@ -26,6 +26,7 @@ public:
|
|||
|
||||
QPair<bool, QFuture<void>> run(std::function<void()> function) noexcept;
|
||||
// QPair<bool, QFuture<QJSValueList>> run(std::function<QJSValueList()> function, const QJSValue &callback);
|
||||
bool stopping() const noexcept;
|
||||
|
||||
private:
|
||||
bool add() noexcept;
|
||||
|
@ -62,7 +63,7 @@ private:
|
|||
size_t Alive;
|
||||
QWaitCondition Condition;
|
||||
QMutex Mutex;
|
||||
bool Stopping;
|
||||
std::atomic<bool> Stopping;
|
||||
};
|
||||
|
||||
#endif // FUTURE_SCHEDULER_H
|
||||
|
|
Loading…
Reference in a new issue