diff --git a/monero b/monero index f7705c2..772d207 160000 --- a/monero +++ b/monero @@ -1 +1 @@ -Subproject commit f7705c2c6740699a3fa47895473f79c006624559 +Subproject commit 772d207026dac31f927efa733fda3f73b103e71a diff --git a/src/CoinsWidget.cpp b/src/CoinsWidget.cpp index 955e7c9..4833ac8 100644 --- a/src/CoinsWidget.cpp +++ b/src/CoinsWidget.cpp @@ -9,11 +9,12 @@ #include "dialog/OutputInfoDialog.h" #include "dialog/OutputSweepDialog.h" #include "utils/Icons.h" +#include "utils/Utils.h" -CoinsWidget::CoinsWidget(QSharedPointer ctx, QWidget *parent) +CoinsWidget::CoinsWidget(Wallet *wallet, QWidget *parent) : QWidget(parent) , ui(new Ui::CoinsWidget) - , m_ctx(std::move(ctx)) + , m_wallet(wallet) , m_headerMenu(new QMenu(this)) , m_copyMenu(new QMenu("Copy",this)) { @@ -71,7 +72,7 @@ CoinsWidget::CoinsWidget(QSharedPointer ctx, QWidget *parent) connect(ui->search, &QLineEdit::textChanged, this, &CoinsWidget::setSearchFilter); - connect(m_ctx.get(), &AppContext::selectedInputsChanged, this, &CoinsWidget::selectCoins); + connect(m_wallet, &Wallet::selectedInputsChanged, this, &CoinsWidget::selectCoins); } void CoinsWidget::setModel(CoinsModel * model, Coins * coins) { @@ -84,7 +85,7 @@ void CoinsWidget::setModel(CoinsModel * model, Coins * coins) { ui->coins->setColumnHidden(CoinsModel::SpentHeight, true); ui->coins->setColumnHidden(CoinsModel::Frozen, true); - if (!m_ctx->wallet->viewOnly()) { + if (!m_wallet->viewOnly()) { ui->coins->setColumnHidden(CoinsModel::KeyImageKnown, true); } else { ui->coins->setColumnHidden(CoinsModel::KeyImageKnown, false); @@ -188,7 +189,7 @@ void CoinsWidget::spendSelected() { keyimages << m_model->entryFromIndex(m_proxyModel->mapToSource(index))->keyImage(); } - m_ctx->setSelectedInputs(keyimages); + m_wallet->setSelectedInputs(keyimages); this->selectCoins(keyimages); } @@ -239,7 +240,7 @@ void CoinsWidget::onSweepOutputs() { int ret = dialog.exec(); if (!ret) return; - m_ctx->onSweepOutputs(keyImages, dialog.address(), dialog.churn(), dialog.outputs()); + m_wallet->sweepOutputs(keyImages, dialog.address(), dialog.churn(), dialog.outputs()); } void CoinsWidget::copy(copyField field) { @@ -298,18 +299,18 @@ QVector CoinsWidget::currentEntries() { void CoinsWidget::freezeCoins(QStringList &pubkeys) { for (auto &pubkey : pubkeys) { - m_ctx->wallet->coins()->freeze(pubkey); + m_wallet->coins()->freeze(pubkey); } - m_ctx->wallet->coins()->refresh(m_ctx->wallet->currentSubaddressAccount()); - m_ctx->updateBalance(); + m_wallet->coins()->refresh(m_wallet->currentSubaddressAccount()); + m_wallet->updateBalance(); } void CoinsWidget::thawCoins(QStringList &pubkeys) { for (auto &pubkey : pubkeys) { - m_ctx->wallet->coins()->thaw(pubkey); + m_wallet->coins()->thaw(pubkey); } - m_ctx->wallet->coins()->refresh(m_ctx->wallet->currentSubaddressAccount()); - m_ctx->updateBalance(); + m_wallet->coins()->refresh(m_wallet->currentSubaddressAccount()); + m_wallet->updateBalance(); } void CoinsWidget::selectCoins(const QStringList &keyimages) { diff --git a/src/CoinsWidget.h b/src/CoinsWidget.h index f598e98..15d06d5 100644 --- a/src/CoinsWidget.h +++ b/src/CoinsWidget.h @@ -8,10 +8,10 @@ #include #include -#include "appcontext.h" #include "model/CoinsModel.h" #include "model/CoinsProxyModel.h" #include "libwalletqt/Coins.h" +#include "libwalletqt/Wallet.h" namespace Ui { class CoinsWidget; @@ -22,7 +22,7 @@ class CoinsWidget : public QWidget Q_OBJECT public: - explicit CoinsWidget(QSharedPointer ctx, QWidget *parent = nullptr); + explicit CoinsWidget(Wallet *wallet, QWidget *parent = nullptr); void setModel(CoinsModel * model, Coins * coins); ~CoinsWidget() override; @@ -62,7 +62,7 @@ private: }; QScopedPointer ui; - QSharedPointer m_ctx; + Wallet *m_wallet; QMenu *m_contextMenu; QMenu *m_headerMenu; diff --git a/src/ContactsWidget.cpp b/src/ContactsWidget.cpp index a438ed4..47707f2 100644 --- a/src/ContactsWidget.cpp +++ b/src/ContactsWidget.cpp @@ -8,13 +8,14 @@ #include "dialog/ContactsDialog.h" #include "libwalletqt/AddressBook.h" +#include "libwalletqt/WalletManager.h" #include "model/ModelUtils.h" #include "utils/Icons.h" -ContactsWidget::ContactsWidget(QSharedPointer ctx, QWidget *parent) +ContactsWidget::ContactsWidget(Wallet *wallet, QWidget *parent) : QWidget(parent) , ui(new Ui::ContactsWidget) - , m_ctx(std::move(ctx)) + , m_wallet(wallet) { ui->setupUi(this); @@ -23,7 +24,7 @@ ContactsWidget::ContactsWidget(QSharedPointer ctx, QWidget *parent) ui->searchLayout->addWidget(m_btn_addContact, 0, Qt::AlignRight); connect(m_btn_addContact, &QPushButton::clicked, [this]{this->newContact();}); - m_model = m_ctx->wallet->addressBookModel(); + m_model = m_wallet->addressBookModel(); m_proxyModel = new AddressBookProxyModel; m_proxyModel->setSourceModel(m_model); ui->contacts->setModel(m_proxyModel); @@ -124,17 +125,17 @@ void ContactsWidget::newContact(QString address, QString name) address = dialog.getAddress(); name = dialog.getName(); - bool addressValid = WalletManager::addressValid(address, m_ctx->wallet->nettype()); + bool addressValid = WalletManager::addressValid(address, m_wallet->nettype()); if (!addressValid) { QMessageBox::warning(this, "Invalid address", "Invalid address"); return; } - int num_addresses = m_ctx->wallet->addressBook()->count(); + int num_addresses = m_wallet->addressBook()->count(); QString address_entry; QString name_entry; for (int i=0; iwallet->addressBook()->getRow(i, [&address_entry, &name_entry](const AddressBookInfo &entry){ + m_wallet->addressBook()->getRow(i, [&address_entry, &name_entry](const AddressBookInfo &entry){ address_entry = entry.address(); name_entry = entry.description(); }); @@ -151,7 +152,7 @@ void ContactsWidget::newContact(QString address, QString name) } } - m_ctx->wallet->addressBook()->addRow(address, "", name); + m_wallet->addressBook()->addRow(address, "", name); } void ContactsWidget::deleteContact() diff --git a/src/ContactsWidget.h b/src/ContactsWidget.h index 704a9ef..27e743a 100644 --- a/src/ContactsWidget.h +++ b/src/ContactsWidget.h @@ -8,9 +8,9 @@ #include #include -#include "appcontext.h" #include "model/AddressBookModel.h" #include "model/AddressBookProxyModel.h" +#include "libwalletqt/Wallet.h" namespace Ui { class ContactsWidget; @@ -21,7 +21,7 @@ class ContactsWidget : public QWidget Q_OBJECT public: - explicit ContactsWidget(QSharedPointer ctx, QWidget *parent = nullptr); + explicit ContactsWidget(Wallet *wallet, QWidget *parent = nullptr); ~ContactsWidget() override; void setSearchbarVisible(bool visible); @@ -44,7 +44,7 @@ private slots: private: QScopedPointer ui; - QSharedPointer m_ctx; + Wallet *m_wallet; QAction *m_showFullAddressesAction; QMenu *m_rowMenu; diff --git a/src/HistoryWidget.cpp b/src/HistoryWidget.cpp index e272894..d314563 100644 --- a/src/HistoryWidget.cpp +++ b/src/HistoryWidget.cpp @@ -6,20 +6,20 @@ #include -#include "appcontext.h" #include "dialog/TxInfoDialog.h" #include "dialog/TxProofDialog.h" +#include "libwalletqt/WalletManager.h" #include "utils/config.h" #include "utils/Icons.h" #include "WebsocketNotifier.h" -HistoryWidget::HistoryWidget(QSharedPointer ctx, QWidget *parent) +HistoryWidget::HistoryWidget(Wallet *wallet, QWidget *parent) : QWidget(parent) , ui(new Ui::HistoryWidget) - , m_ctx(std::move(ctx)) + , m_wallet(wallet) , m_contextMenu(new QMenu(this)) , m_copyMenu(new QMenu("Copy", this)) - , m_model(m_ctx->wallet->historyModel()) + , m_model(wallet->historyModel()) { ui->setupUi(this); m_contextMenu->addMenu(m_copyMenu); @@ -49,7 +49,7 @@ HistoryWidget::HistoryWidget(QSharedPointer ctx, QWidget *parent) ui->syncNotice->hide(); }); - connect(m_ctx.get(), &AppContext::walletRefreshed, this, &HistoryWidget::onWalletRefreshed); + connect(m_wallet, &Wallet::walletRefreshed, this, &HistoryWidget::onWalletRefreshed); ui->syncNotice->setVisible(config()->get(Config::showHistorySyncNotice).toBool()); ui->history->setHistoryModel(m_model); @@ -119,7 +119,7 @@ void HistoryWidget::showTxDetails() { auto *tx = ui->history->currentEntry(); if (!tx) return; - auto *dialog = new TxInfoDialog(m_ctx, tx, this); + auto *dialog = new TxInfoDialog(m_wallet, tx, this); connect(dialog, &TxInfoDialog::resendTranscation, [this](const QString &txid){ emit resendTransaction(txid); }); @@ -148,7 +148,7 @@ void HistoryWidget::createTxProof() { auto *tx = ui->history->currentEntry(); if (!tx) return; - TxProofDialog dialog{this, m_ctx, tx}; + TxProofDialog dialog{this, m_wallet, tx}; dialog.getTxKey(); dialog.exec(); } diff --git a/src/HistoryWidget.h b/src/HistoryWidget.h index 6399dd9..0ac765b 100644 --- a/src/HistoryWidget.h +++ b/src/HistoryWidget.h @@ -7,7 +7,6 @@ #include #include -#include "appcontext.h" #include "libwalletqt/Coins.h" #include "libwalletqt/Wallet.h" #include "model/TransactionHistoryModel.h" @@ -22,7 +21,7 @@ class HistoryWidget : public QWidget Q_OBJECT public: - explicit HistoryWidget(QSharedPointer ctx, QWidget *parent = nullptr); + explicit HistoryWidget(Wallet *wallet, QWidget *parent = nullptr); ~HistoryWidget() override; void setSearchbarVisible(bool visible); @@ -58,7 +57,7 @@ private: void showSyncNoticeMsg(); QScopedPointer ui; - QSharedPointer m_ctx; + Wallet *m_wallet; QMenu *m_contextMenu; QMenu *m_copyMenu; TransactionHistoryProxyModel *m_model; diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 1ea4e51..2b63851 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -43,7 +43,9 @@ MainWindow::MainWindow(WindowManager *windowManager, Wallet *wallet, QWidget *pa : QMainWindow(parent) , ui(new Ui::MainWindow) , m_windowManager(windowManager) - , m_ctx(new AppContext(wallet)) + , m_wallet(wallet) + , m_nodes(new Nodes(this, wallet)) + , m_rpc(new DaemonRpc(this, "")) { ui->setupUi(this); @@ -55,7 +57,7 @@ MainWindow::MainWindow(WindowManager *windowManager, Wallet *wallet, QWidget *pa m_windowCalc = new CalcWindow(this); m_splashDialog = new SplashDialog(this); - m_accountSwitcherDialog = new AccountSwitcherDialog(m_ctx, this); + m_accountSwitcherDialog = new AccountSwitcherDialog(m_wallet, this); m_updater = QSharedPointer(new Updater(this)); @@ -81,18 +83,9 @@ MainWindow::MainWindow(WindowManager *windowManager, Wallet *wallet, QWidget *pa connect(m_windowManager, &WindowManager::websocketStatusChanged, this, &MainWindow::onWebsocketStatusChanged); this->onWebsocketStatusChanged(!config()->get(Config::disableWebsocket).toBool()); - connect(m_windowManager, &WindowManager::proxySettingsChanged, [this]{ - m_ctx->onProxySettingsChanged(); - this->onProxySettingsChanged(); - }); - connect(m_windowManager, &WindowManager::updateBalance, m_ctx.data(), &AppContext::updateBalance); - connect(m_windowManager, &WindowManager::offlineMode, [this](bool offline){ - if (!m_ctx->wallet) { - return; - } - m_ctx->wallet->setOffline(offline); - this->onConnectionStatusChanged(Wallet::ConnectionStatus_Disconnected); - }); + connect(m_windowManager, &WindowManager::proxySettingsChanged, this, &MainWindow::onProxySettingsChanged); + connect(m_windowManager, &WindowManager::updateBalance, m_wallet, &Wallet::updateBalance); + connect(m_windowManager, &WindowManager::offlineMode, this, &MainWindow::onOfflineMode); connect(torManager(), &TorManager::connectionStateChanged, this, &MainWindow::onTorConnectionStateChanged); this->onTorConnectionStateChanged(torManager()->torConnected); @@ -191,20 +184,20 @@ void MainWindow::initWidgets() { ui->tabHomeWidget->setCurrentIndex(TabsHome(homeWidget)); // [History] - m_historyWidget = new HistoryWidget(m_ctx, this); + m_historyWidget = new HistoryWidget(m_wallet, this); ui->historyWidgetLayout->addWidget(m_historyWidget); connect(m_historyWidget, &HistoryWidget::viewOnBlockExplorer, this, &MainWindow::onViewOnBlockExplorer); connect(m_historyWidget, &HistoryWidget::resendTransaction, this, &MainWindow::onResendTransaction); // [Send] - m_sendWidget = new SendWidget(m_ctx, this); + m_sendWidget = new SendWidget(m_wallet, this); ui->sendWidgetLayout->addWidget(m_sendWidget); // -------------- - m_contactsWidget = new ContactsWidget(m_ctx, this); + m_contactsWidget = new ContactsWidget(m_wallet, this); ui->contactsWidgetLayout->addWidget(m_contactsWidget); // [Receive] - m_receiveWidget = new ReceiveWidget(m_ctx, this); + m_receiveWidget = new ReceiveWidget(m_wallet, this); ui->receiveWidgetLayout->addWidget(m_receiveWidget); connect(m_receiveWidget, &ReceiveWidget::showTransactions, [this](const QString &text) { m_historyWidget->setSearchText(text); @@ -213,18 +206,18 @@ void MainWindow::initWidgets() { connect(m_contactsWidget, &ContactsWidget::fillAddress, m_sendWidget, &SendWidget::fillAddress); // [Coins] - m_coinsWidget = new CoinsWidget(m_ctx, this); + m_coinsWidget = new CoinsWidget(m_wallet, this); ui->coinsWidgetLayout->addWidget(m_coinsWidget); #ifdef HAS_LOCALMONERO - m_localMoneroWidget = new LocalMoneroWidget(this, m_ctx); + m_localMoneroWidget = new LocalMoneroWidget(this, m_wallet); ui->localMoneroLayout->addWidget(m_localMoneroWidget); #else ui->tabWidgetExchanges->setTabVisible(0, false); #endif #ifdef HAS_XMRIG - m_xmrig = new XMRigWidget(m_ctx, this); + m_xmrig = new XMRigWidget(m_wallet, this); ui->xmrRigLayout->addWidget(m_xmrig); connect(m_xmrig, &XMRigWidget::miningStarted, [this]{ this->updateTitle(); }); @@ -239,7 +232,7 @@ void MainWindow::initWidgets() { ui->frame_coinControl->setVisible(false); connect(ui->btn_resetCoinControl, &QPushButton::clicked, [this]{ - m_ctx->setSelectedInputs({}); + m_wallet->setSelectedInputs({}); }); m_walletUnlockWidget = new WalletUnlockWidget(this); @@ -276,8 +269,8 @@ void MainWindow::initMenu() { // [Wallet] -> [Advanced] connect(ui->actionStore_wallet, &QAction::triggered, this, &MainWindow::tryStoreWallet); - connect(ui->actionUpdate_balance, &QAction::triggered, [this]{m_ctx->updateBalance();}); - connect(ui->actionRefresh_tabs, &QAction::triggered, [this]{m_ctx->refreshModels();}); + connect(ui->actionUpdate_balance, &QAction::triggered, [this]{m_wallet->updateBalance();}); + connect(ui->actionRefresh_tabs, &QAction::triggered, [this]{m_wallet->refreshModels();}); connect(ui->actionRescan_spent, &QAction::triggered, this, &MainWindow::rescanSpent); connect(ui->actionWallet_cache_debug, &QAction::triggered, this, &MainWindow::showWalletCacheDebugDialog); @@ -399,14 +392,14 @@ void MainWindow::initMenu() { void MainWindow::initHome() { // Ticker widgets - m_tickerWidgets.append(new PriceTickerWidget(this, m_ctx, "XMR")); - m_tickerWidgets.append(new PriceTickerWidget(this, m_ctx, "BTC")); - m_tickerWidgets.append(new RatioTickerWidget(this, m_ctx, "XMR", "BTC")); + m_tickerWidgets.append(new PriceTickerWidget(this, m_wallet, "XMR")); + m_tickerWidgets.append(new PriceTickerWidget(this, m_wallet, "BTC")); + m_tickerWidgets.append(new RatioTickerWidget(this, m_wallet, "XMR", "BTC")); for (const auto &widget : m_tickerWidgets) { ui->tickerLayout->addWidget(widget); } - m_balanceTickerWidget = new BalanceTickerWidget(this, m_ctx, false); + m_balanceTickerWidget = new BalanceTickerWidget(this, m_wallet, false); ui->fiatTickerLayout->addWidget(m_balanceTickerWidget); connect(ui->ccsWidget, &CCSWidget::selected, this, &MainWindow::showSendScreen); @@ -419,29 +412,56 @@ void MainWindow::initHome() { } void MainWindow::initWalletContext() { - connect(m_ctx.get(), &AppContext::balanceUpdated, this, &MainWindow::onBalanceUpdated); - connect(m_ctx.get(), &AppContext::synchronized, this, &MainWindow::onSynchronized); - connect(m_ctx.get(), &AppContext::blockchainSync, this, &MainWindow::onBlockchainSync); - connect(m_ctx.get(), &AppContext::refreshSync, this, &MainWindow::onRefreshSync); - connect(m_ctx.get(), &AppContext::createTransactionError, this, &MainWindow::onCreateTransactionError); - connect(m_ctx.get(), &AppContext::createTransactionSuccess, this, &MainWindow::onCreateTransactionSuccess); - connect(m_ctx.get(), &AppContext::transactionCommitted, this, &MainWindow::onTransactionCommitted); - connect(m_ctx.get(), &AppContext::deviceError, this, &MainWindow::onDeviceError); - connect(m_ctx.get(), &AppContext::deviceButtonRequest, this, &MainWindow::onDeviceButtonRequest); - connect(m_ctx.get(), &AppContext::deviceButtonPressed, this, &MainWindow::onDeviceButtonPressed); - connect(m_ctx.get(), &AppContext::initiateTransaction, this, &MainWindow::onInitiateTransaction); - connect(m_ctx.get(), &AppContext::endTransaction, this, &MainWindow::onEndTransaction); - connect(m_ctx.get(), &AppContext::keysCorrupted, this, &MainWindow::onKeysCorrupted); - connect(m_ctx.get(), &AppContext::selectedInputsChanged, this, &MainWindow::onSelectedInputsChanged); + connect(m_wallet, &Wallet::balanceUpdated, this, &MainWindow::onBalanceUpdated); + connect(m_wallet, &Wallet::synchronized, this, &MainWindow::onSynchronized); //TODO + connect(m_wallet, &Wallet::blockchainSync, this, &MainWindow::onBlockchainSync); + connect(m_wallet, &Wallet::refreshSync, this, &MainWindow::onRefreshSync); + connect(m_wallet, &Wallet::createTransactionError, this, &MainWindow::onCreateTransactionError); + connect(m_wallet, &Wallet::createTransactionSuccess, this, &MainWindow::onCreateTransactionSuccess); + connect(m_wallet, &Wallet::transactionCommitted, this, &MainWindow::onTransactionCommitted); + connect(m_wallet, &Wallet::initiateTransaction, this, &MainWindow::onInitiateTransaction); + connect(m_wallet, &Wallet::endTransaction, this, &MainWindow::onEndTransaction); + connect(m_wallet, &Wallet::keysCorrupted, this, &MainWindow::onKeysCorrupted); + connect(m_wallet, &Wallet::selectedInputsChanged, this, &MainWindow::onSelectedInputsChanged); // Wallet - connect(m_ctx->wallet, &Wallet::connectionStatusChanged, [this](int status){ + connect(m_wallet, &Wallet::connectionStatusChanged, [this](int status){ // Order is important, first inform UI about a potential disconnect, then reconnect this->onConnectionStatusChanged(status); - this->m_ctx->nodes->autoConnect(); + m_nodes->autoConnect(); + }); + connect(m_wallet, &Wallet::currentSubaddressAccountChanged, this, &MainWindow::updateTitle); + connect(m_wallet, &Wallet::walletPassphraseNeeded, this, &MainWindow::onWalletPassphraseNeeded); + + connect(m_wallet, &Wallet::unconfirmedMoneyReceived, this, [this](const QString &txId, uint64_t amount){ + if (m_wallet->isSynchronized()) { + auto notify = QString("%1 XMR (pending)").arg(WalletManager::displayAmount(amount, false)); + Utils::desktopNotify("Payment received", notify, 5000); + } + }); + + // Device + connect(m_wallet, &Wallet::deviceButtonRequest, this, &MainWindow::onDeviceButtonRequest); + connect(m_wallet, &Wallet::deviceButtonPressed, this, &MainWindow::onDeviceButtonPressed); + connect(m_wallet, &Wallet::deviceError, this, &MainWindow::onDeviceError); + + connect(m_wallet, &Wallet::donationSent, this, []{ + config()->set(Config::donateBeg, -1); + }); + + connect(m_wallet, &Wallet::multiBroadcast, this, [this](PendingTransaction *tx){ + quint64 count = tx->txCount(); + for (quint64 i = 0; i < count; i++) { + QString txData = tx->signedTxToHex(i); + + for (const auto& node: m_nodes->nodes()) { + QString address = node.toURL(); + qDebug() << QString("Relaying %1 to: %2").arg(tx->txid()[i], address); + m_rpc->setDaemonAddress(address); + m_rpc->sendRawTransaction(txData); + } + } }); - connect(m_ctx->wallet, &Wallet::currentSubaddressAccountChanged, this, &MainWindow::updateTitle); - connect(m_ctx->wallet, &Wallet::walletPassphraseNeeded, this, &MainWindow::onWalletPassphraseNeeded); } void MainWindow::menuToggleTabVisible(const QString &key){ @@ -459,15 +479,15 @@ void MainWindow::menuClearHistoryClicked() { } QString MainWindow::walletName() { - return QFileInfo(m_ctx->wallet->cachePath()).fileName(); + return QFileInfo(m_wallet->cachePath()).fileName(); } QString MainWindow::walletCachePath() { - return m_ctx->wallet->cachePath(); + return m_wallet->cachePath(); } QString MainWindow::walletKeysPath() { - return m_ctx->wallet->keysPath(); + return m_wallet->keysPath(); } void MainWindow::displayWalletErrorMsg(const QString &err) { @@ -497,10 +517,10 @@ void MainWindow::onWalletOpened() { qDebug() << Q_FUNC_INFO; m_splashDialog->hide(); - m_ctx->wallet->setRingDatabase(Utils::ringDatabasePath()); + m_wallet->setRingDatabase(Utils::ringDatabasePath()); - m_ctx->updateBalance(); - if (m_ctx->wallet->isHwBacked()) { + m_wallet->updateBalance(); + if (m_wallet->isHwBacked()) { m_statusBtnHwDevice->show(); } @@ -508,38 +528,39 @@ void MainWindow::onWalletOpened() { this->setEnabled(true); // receive page - m_ctx->wallet->subaddress()->refresh(m_ctx->wallet->currentSubaddressAccount()); - if (m_ctx->wallet->subaddress()->count() == 1) { + m_wallet->subaddress()->refresh(m_wallet->currentSubaddressAccount()); + if (m_wallet->subaddress()->count() == 1) { for (int i = 0; i < 10; i++) { - m_ctx->wallet->subaddress()->addRow(m_ctx->wallet->currentSubaddressAccount(), ""); + m_wallet->subaddress()->addRow(m_wallet->currentSubaddressAccount(), ""); } } - m_ctx->wallet->subaddressModel()->setCurrentSubaddressAccount(m_ctx->wallet->currentSubaddressAccount()); + m_wallet->subaddressModel()->setCurrentSubaddressAccount(m_wallet->currentSubaddressAccount()); // history page - m_ctx->wallet->history()->refresh(m_ctx->wallet->currentSubaddressAccount()); + m_wallet->history()->refresh(m_wallet->currentSubaddressAccount()); // coins page - m_ctx->wallet->coins()->refresh(m_ctx->wallet->currentSubaddressAccount()); - m_coinsWidget->setModel(m_ctx->wallet->coinsModel(), m_ctx->wallet->coins()); - m_ctx->wallet->coinsModel()->setCurrentSubaddressAccount(m_ctx->wallet->currentSubaddressAccount()); + m_wallet->coins()->refresh(m_wallet->currentSubaddressAccount()); + m_coinsWidget->setModel(m_wallet->coinsModel(), m_wallet->coins()); + m_wallet->coinsModel()->setCurrentSubaddressAccount(m_wallet->currentSubaddressAccount()); // Coin labeling uses set_tx_note, so we need to refresh history too - connect(m_ctx->wallet->coins(), &Coins::descriptionChanged, [this] { - m_ctx->wallet->history()->refresh(m_ctx->wallet->currentSubaddressAccount()); + connect(m_wallet->coins(), &Coins::descriptionChanged, [this] { + m_wallet->history()->refresh(m_wallet->currentSubaddressAccount()); }); // Vice versa - connect(m_ctx->wallet->history(), &TransactionHistory::txNoteChanged, [this] { - m_ctx->wallet->coins()->refresh(m_ctx->wallet->currentSubaddressAccount()); + connect(m_wallet->history(), &TransactionHistory::txNoteChanged, [this] { + m_wallet->coins()->refresh(m_wallet->currentSubaddressAccount()); }); this->updatePasswordIcon(); this->updateTitle(); - m_ctx->nodes->connectToNode(); + m_nodes->allowConnection(); + m_nodes->connectToNode(); m_updateBytes.start(250); if (config()->get(Config::writeRecentlyOpenedWallets).toBool()) { - this->addToRecentlyOpened(m_ctx->wallet->cachePath()); + this->addToRecentlyOpened(m_wallet->cachePath()); } } @@ -587,13 +608,13 @@ void MainWindow::setStatusText(const QString &text, bool override, int timeout) } void MainWindow::tryStoreWallet() { - if (m_ctx->wallet->connectionStatus() == Wallet::ConnectionStatus::ConnectionStatus_Synchronizing) { + if (m_wallet->connectionStatus() == Wallet::ConnectionStatus::ConnectionStatus_Synchronizing) { QMessageBox::warning(this, "Save wallet", "Unable to save wallet during synchronization.\n\n" "Wait until synchronization is finished and try again."); return; } - m_ctx->wallet->store(); + m_wallet->store(); } void MainWindow::onWebsocketStatusChanged(bool enabled) { @@ -614,6 +635,8 @@ void MainWindow::onWebsocketStatusChanged(bool enabled) { } void MainWindow::onProxySettingsChanged() { + m_nodes->connectToNode(); + int proxy = config()->get(Config::proxy).toInt(); if (proxy == Config::Proxy::Tor) { @@ -631,6 +654,14 @@ void MainWindow::onProxySettingsChanged() { m_statusBtnProxySettings->hide(); } +void MainWindow::onOfflineMode(bool offline) { + if (!m_wallet) { + return; + } + m_wallet->setOffline(offline); + this->onConnectionStatusChanged(Wallet::ConnectionStatus_Disconnected); +} + void MainWindow::onSynchronized() { this->updateNetStats(); this->setStatusText("Synchronized"); @@ -693,13 +724,13 @@ void MainWindow::onCreateTransactionSuccess(PendingTransaction *tx, const QVecto QString tx_err = tx->errorString(); qCritical() << tx_err; - if (m_ctx->wallet->connectionStatus() == Wallet::ConnectionStatus_WrongVersion) + if (m_wallet->connectionStatus() == Wallet::ConnectionStatus_WrongVersion) err = QString("%1 Wrong node version: %2").arg(err, tx_err); else err = QString("%1 %2").arg(err, tx_err); if (tx_err.contains("Node response did not include the requested real output")) { - QString currentNode = m_ctx->nodes->connection().toAddress(); + QString currentNode = m_nodes->connection().toAddress(); err += QString("\nYou are currently connected to: %1\n\n" "This node may be acting maliciously. You are strongly recommended to disconnect from this node." @@ -708,21 +739,21 @@ void MainWindow::onCreateTransactionSuccess(PendingTransaction *tx, const QVecto qDebug() << Q_FUNC_INFO << err; this->displayWalletErrorMsg(err); - m_ctx->wallet->disposeTransaction(tx); + m_wallet->disposeTransaction(tx); return; } else if (tx->txCount() == 0) { err = QString("%1 %2").arg(err, "No unmixable outputs to sweep."); qDebug() << Q_FUNC_INFO << err; this->displayWalletErrorMsg(err); - m_ctx->wallet->disposeTransaction(tx); + m_wallet->disposeTransaction(tx); return; } else if (tx->txCount() > 1) { err = QString("%1 %2").arg(err, "Split transactions are not supported. Try sending a smaller amount."); qDebug() << Q_FUNC_INFO << err; this->displayWalletErrorMsg(err); - m_ctx->wallet->disposeTransaction(tx); + m_wallet->disposeTransaction(tx); return; } @@ -743,35 +774,36 @@ void MainWindow::onCreateTransactionSuccess(PendingTransaction *tx, const QVecto err = QString("%1 %2").arg(err, "Constructed transaction doesn't appear to send to (all) specified destination address(es). Try creating the transaction again."); qDebug() << Q_FUNC_INFO << err; this->displayWalletErrorMsg(err); - m_ctx->wallet->disposeTransaction(tx); + m_wallet->disposeTransaction(tx); return; } - m_ctx->addCacheTransaction(tx->txid()[0], tx->signedTxToHex(0)); + m_wallet->addCacheTransaction(tx->txid()[0], tx->signedTxToHex(0)); // Show advanced dialog on multi-destination transactions - if (address.size() > 1 || m_ctx->wallet->viewOnly()) { - TxConfAdvDialog dialog_adv{m_ctx, m_ctx->tmpTxDescription, this}; - dialog_adv.setTransaction(tx, !m_ctx->wallet->viewOnly()); + if (address.size() > 1 || m_wallet->viewOnly()) { + TxConfAdvDialog dialog_adv{m_wallet, m_wallet->tmpTxDescription, this}; + dialog_adv.setTransaction(tx, !m_wallet->viewOnly()); dialog_adv.exec(); return; } - TxConfDialog dialog{m_ctx, tx, address[0], m_ctx->tmpTxDescription, this}; + TxConfDialog dialog{m_wallet, tx, address[0], m_wallet->tmpTxDescription, this}; switch (dialog.exec()) { case QDialog::Rejected: { - if (!dialog.showAdvanced) - m_ctx->onCancelTransaction(tx, address); + if (!dialog.showAdvanced) { + m_wallet->disposeTransaction(tx); + } break; } case QDialog::Accepted: - m_ctx->commitTransaction(tx, m_ctx->tmpTxDescription); + m_wallet->commitTransaction(tx, m_wallet->tmpTxDescription); break; } if (dialog.showAdvanced) { - TxConfAdvDialog dialog_adv{m_ctx, m_ctx->tmpTxDescription, this}; + TxConfAdvDialog dialog_adv{m_wallet, m_wallet->tmpTxDescription, this}; dialog_adv.setTransaction(tx); dialog_adv.exec(); } @@ -789,8 +821,8 @@ void MainWindow::onTransactionCommitted(bool status, PendingTransaction *tx, con msgBox.exec(); if (msgBox.clickedButton() == showDetailsButton) { this->showHistoryTab(); - TransactionInfo *txInfo = m_ctx->wallet->history()->transaction(txid.first()); - auto *dialog = new TxInfoDialog(m_ctx, txInfo, this); + TransactionInfo *txInfo = m_wallet->history()->transaction(txid.first()); + auto *dialog = new TxInfoDialog(m_wallet, txInfo, this); connect(dialog, &TxInfoDialog::resendTranscation, this, &MainWindow::onResendTransaction); dialog->show(); dialog->setAttribute(Qt::WA_DeleteOnClose); @@ -815,22 +847,22 @@ void MainWindow::onCreateTransactionError(const QString &message) { } void MainWindow::showWalletInfoDialog() { - WalletInfoDialog dialog{m_ctx, this}; + WalletInfoDialog dialog{m_wallet, this}; dialog.exec(); } void MainWindow::showSeedDialog() { - if (m_ctx->wallet->isHwBacked()) { + if (m_wallet->isHwBacked()) { QMessageBox::information(this, "Information", "Seed unavailable: Wallet keys are stored on hardware device."); return; } - if (m_ctx->wallet->viewOnly()) { + if (m_wallet->viewOnly()) { QMessageBox::information(this, "Information", "Wallet is view-only and has no seed.\n\nTo obtain wallet keys go to Wallet -> View-Only"); return; } - if (!m_ctx->wallet->isDeterministic()) { + if (!m_wallet->isDeterministic()) { QMessageBox::information(this, "Information", "Wallet is non-deterministic and has no seed.\n\nTo obtain wallet keys go to Wallet -> Keys"); return; } @@ -839,18 +871,18 @@ void MainWindow::showSeedDialog() { return; } - SeedDialog dialog{m_ctx, this}; + SeedDialog dialog{m_wallet, this}; dialog.exec(); } void MainWindow::showPasswordDialog() { - PasswordChangeDialog dialog{this, m_ctx->wallet}; + PasswordChangeDialog dialog{this, m_wallet}; dialog.exec(); this->updatePasswordIcon(); } void MainWindow::updatePasswordIcon() { - bool emptyPassword = m_ctx->wallet->verifyPassword(""); + bool emptyPassword = m_wallet->verifyPassword(""); QIcon icon = emptyPassword ? icons()->icon("unlock.svg") : icons()->icon("lock.svg"); m_statusBtnPassword->setIcon(icon); } @@ -860,12 +892,12 @@ void MainWindow::showKeysDialog() { return; } - KeysDialog dialog{m_ctx, this}; + KeysDialog dialog{m_wallet, this}; dialog.exec(); } void MainWindow::showViewOnlyDialog() { - ViewOnlyDialog dialog{m_ctx, this}; + ViewOnlyDialog dialog{m_wallet, this}; dialog.exec(); } @@ -900,16 +932,16 @@ void MainWindow::menuAboutClicked() { } void MainWindow::menuSettingsClicked(bool showProxyTab) { - m_windowManager->showSettings(m_ctx, this, showProxyTab); + m_windowManager->showSettings(m_nodes, this, showProxyTab); } void MainWindow::menuSignVerifyClicked() { - SignVerifyDialog dialog{m_ctx->wallet, this}; + SignVerifyDialog dialog{m_wallet, this}; dialog.exec(); } void MainWindow::menuVerifyTxProof() { - VerifyProofDialog dialog{m_ctx->wallet, this}; + VerifyProofDialog dialog{m_wallet, this}; dialog.exec(); } @@ -936,18 +968,18 @@ void MainWindow::updateWidgetIcons() { QIcon MainWindow::hardwareDevicePairedIcon() { QString filename; - if (m_ctx->wallet->isLedger()) + if (m_wallet->isLedger()) filename = "ledger.png"; - else if (m_ctx->wallet->isTrezor()) + else if (m_wallet->isTrezor()) filename = ColorScheme::darkScheme ? "trezor_white.png" : "trezor.png"; return icons()->icon(filename); } QIcon MainWindow::hardwareDeviceUnpairedIcon() { QString filename; - if (m_ctx->wallet->isLedger()) + if (m_wallet->isLedger()) filename = "ledger_unpaired.png"; - else if (m_ctx->wallet->isTrezor()) + else if (m_wallet->isTrezor()) filename = ColorScheme::darkScheme ? "trezor_unpaired_white.png" : "trezor_unpaired.png"; return icons()->icon(filename); } @@ -964,10 +996,10 @@ void MainWindow::closeEvent(QCloseEvent *event) { m_updateBytes.stop(); m_txTimer.stop(); - m_ctx->stopTimers(); // Wallet signal may fire after AppContext is gone, causing segv - m_ctx->wallet->disconnect(); + m_wallet->disconnect(); + this->disconnect(); this->saveGeo(); m_windowManager->closeWindow(this); @@ -1031,16 +1063,16 @@ void MainWindow::onViewOnBlockExplorer(const QString &txid) { } void MainWindow::onResendTransaction(const QString &txid) { - QString txHex = m_ctx->getCacheTransaction(txid); + QString txHex = m_wallet->getCacheTransaction(txid); if (txHex.isEmpty()) { QMessageBox::warning(this, "Unable to resend transaction", "Transaction was not found in transaction cache. Unable to resend."); return; } // Connect to a different node so chances of successful relay are higher - m_ctx->nodes->autoConnect(true); + m_nodes->autoConnect(true); - TxBroadcastDialog dialog{this, m_ctx, txHex}; + TxBroadcastDialog dialog{this, m_nodes, txHex}; dialog.exec(); } @@ -1048,14 +1080,14 @@ void MainWindow::importContacts() { const QString targetFile = QFileDialog::getOpenFileName(this, "Import CSV file", QDir::homePath(), "CSV Files (*.csv)"); if(targetFile.isEmpty()) return; - auto *model = m_ctx->wallet->addressBookModel(); + auto *model = m_wallet->addressBookModel(); QMapIterator i(model->readCSV(targetFile)); int inserts = 0; while (i.hasNext()) { i.next(); - bool addressValid = WalletManager::addressValid(i.value(), m_ctx->wallet->nettype()); + bool addressValid = WalletManager::addressValid(i.value(), m_wallet->nettype()); if(addressValid) { - m_ctx->wallet->addressBook()->addRow(i.value(), "", i.key()); + m_wallet->addressBook()->addRow(i.value(), "", i.key()); inserts++; } } @@ -1075,7 +1107,7 @@ void MainWindow::restoreGeo() { } void MainWindow::showDebugInfo() { - DebugInfoDialog dialog{m_ctx, this}; + DebugInfoDialog dialog{m_wallet, m_nodes, this}; dialog.exec(); } @@ -1084,7 +1116,7 @@ void MainWindow::showWalletCacheDebugDialog() { return; } - WalletCacheDebugDialog dialog{m_ctx, this}; + WalletCacheDebugDialog dialog{m_wallet, this}; dialog.exec(); } @@ -1103,7 +1135,7 @@ void MainWindow::showAddressChecker() { return; } - SubaddressIndex index = m_ctx->wallet->subaddressIndex(address); + SubaddressIndex index = m_wallet->subaddressIndex(address); if (!index.isValid()) { // TODO: probably mention lookahead here QMessageBox::warning(this, "Address Checker", "This address does not belong to this wallet."); @@ -1117,9 +1149,9 @@ void MainWindow::exportKeyImages() { QString fn = QFileDialog::getSaveFileName(this, "Save key images to file", QString("%1/%2_%3").arg(QDir::homePath(), this->walletName(), QString::number(QDateTime::currentSecsSinceEpoch())), "Key Images (*_keyImages)"); if (fn.isEmpty()) return; if (!fn.endsWith("_keyImages")) fn += "_keyImages"; - bool r = m_ctx->wallet->exportKeyImages(fn, true); + bool r = m_wallet->exportKeyImages(fn, true); if (!r) { - QMessageBox::warning(this, "Key image export", QString("Failed to export key images.\nReason: %1").arg(m_ctx->wallet->errorString())); + QMessageBox::warning(this, "Key image export", QString("Failed to export key images.\nReason: %1").arg(m_wallet->errorString())); } else { QMessageBox::information(this, "Key image export", "Successfully exported key images."); } @@ -1128,12 +1160,12 @@ void MainWindow::exportKeyImages() { void MainWindow::importKeyImages() { QString fn = QFileDialog::getOpenFileName(this, "Import key image file", QDir::homePath(), "Key Images (*_keyImages)"); if (fn.isEmpty()) return; - bool r = m_ctx->wallet->importKeyImages(fn); + bool r = m_wallet->importKeyImages(fn); if (!r) { - QMessageBox::warning(this, "Key image import", QString("Failed to import key images.\n\n%1").arg(m_ctx->wallet->errorString())); + QMessageBox::warning(this, "Key image import", QString("Failed to import key images.\n\n%1").arg(m_wallet->errorString())); } else { QMessageBox::information(this, "Key image import", "Successfully imported key images"); - m_ctx->refreshModels(); + m_wallet->refreshModels(); } } @@ -1141,9 +1173,9 @@ void MainWindow::exportOutputs() { QString fn = QFileDialog::getSaveFileName(this, "Save outputs to file", QString("%1/%2_%3").arg(QDir::homePath(), this->walletName(), QString::number(QDateTime::currentSecsSinceEpoch())), "Outputs (*_outputs)"); if (fn.isEmpty()) return; if (!fn.endsWith("_outputs")) fn += "_outputs"; - bool r = m_ctx->wallet->exportOutputs(fn, true); + bool r = m_wallet->exportOutputs(fn, true); if (!r) { - QMessageBox::warning(this, "Outputs export", QString("Failed to export outputs.\nReason: %1").arg(m_ctx->wallet->errorString())); + QMessageBox::warning(this, "Outputs export", QString("Failed to export outputs.\nReason: %1").arg(m_wallet->errorString())); } else { QMessageBox::information(this, "Outputs export", "Successfully exported outputs."); } @@ -1152,20 +1184,20 @@ void MainWindow::exportOutputs() { void MainWindow::importOutputs() { QString fn = QFileDialog::getOpenFileName(this, "Import outputs file", QDir::homePath(), "Outputs (*_outputs)"); if (fn.isEmpty()) return; - bool r = m_ctx->wallet->importOutputs(fn); + bool r = m_wallet->importOutputs(fn); if (!r) { - QMessageBox::warning(this, "Outputs import", QString("Failed to import outputs.\n\n%1").arg(m_ctx->wallet->errorString())); + QMessageBox::warning(this, "Outputs import", QString("Failed to import outputs.\n\n%1").arg(m_wallet->errorString())); } else { QMessageBox::information(this, "Outputs import", "Successfully imported outputs"); - m_ctx->refreshModels(); + m_wallet->refreshModels(); } } void MainWindow::loadUnsignedTx() { QString fn = QFileDialog::getOpenFileName(this, "Select transaction to load", QDir::homePath(), "Transaction (*unsigned_monero_tx)"); if (fn.isEmpty()) return; - UnsignedTransaction *tx = m_ctx->wallet->loadTxFile(fn); - auto err = m_ctx->wallet->errorString(); + UnsignedTransaction *tx = m_wallet->loadTxFile(fn); + auto err = m_wallet->errorString(); if (!err.isEmpty()) { QMessageBox::warning(this, "Load transaction from file", QString("Failed to load transaction.\n\n%1").arg(err)); return; @@ -1180,8 +1212,8 @@ void MainWindow::loadUnsignedTxFromClipboard() { QMessageBox::warning(this, "Load unsigned transaction from clipboard", "Clipboard is empty"); return; } - UnsignedTransaction *tx = m_ctx->wallet->loadTxFromBase64Str(unsigned_tx); - auto err = m_ctx->wallet->errorString(); + UnsignedTransaction *tx = m_wallet->loadTxFromBase64Str(unsigned_tx); + auto err = m_wallet->errorString(); if (!err.isEmpty()) { QMessageBox::warning(this, "Load unsigned transaction from clipboard", QString("Failed to load transaction.\n\n%1").arg(err)); return; @@ -1193,25 +1225,25 @@ void MainWindow::loadUnsignedTxFromClipboard() { void MainWindow::loadSignedTx() { QString fn = QFileDialog::getOpenFileName(this, "Select transaction to load", QDir::homePath(), "Transaction (*signed_monero_tx)"); if (fn.isEmpty()) return; - PendingTransaction *tx = m_ctx->wallet->loadSignedTxFile(fn); - auto err = m_ctx->wallet->errorString(); + PendingTransaction *tx = m_wallet->loadSignedTxFile(fn); + auto err = m_wallet->errorString(); if (!err.isEmpty()) { QMessageBox::warning(this, "Load signed transaction from file", err); return; } - TxConfAdvDialog dialog{m_ctx, "", this}; + TxConfAdvDialog dialog{m_wallet, "", this}; dialog.setTransaction(tx); dialog.exec(); } void MainWindow::loadSignedTxFromText() { - TxBroadcastDialog dialog{this, m_ctx}; + TxBroadcastDialog dialog{this, m_nodes}; dialog.exec(); } void MainWindow::createUnsignedTxDialog(UnsignedTransaction *tx) { - TxConfAdvDialog dialog{m_ctx, "", this}; + TxConfAdvDialog dialog{m_wallet, "", this}; dialog.setUnsignedTransaction(tx); dialog.exec(); } @@ -1229,11 +1261,13 @@ void MainWindow::importTransaction() { } } - TxImportDialog dialog(this, m_ctx); + TxImportDialog dialog(this, m_wallet); dialog.exec(); } void MainWindow::onDeviceError(const QString &error) { + qCritical() << "Device error: " << error; + if (m_showDeviceError) { return; } @@ -1243,7 +1277,7 @@ void MainWindow::onDeviceError(const QString &error) { m_showDeviceError = true; auto result = QMessageBox::question(this, "Hardware device", "Lost connection to hardware device. Attempt to reconnect?"); if (result == QMessageBox::Yes) { - bool r = m_ctx->wallet->reconnectDevice(); + bool r = m_wallet->reconnectDevice(); if (r) { break; } @@ -1254,14 +1288,14 @@ void MainWindow::onDeviceError(const QString &error) { } } m_statusBtnHwDevice->setIcon(this->hardwareDevicePairedIcon()); - m_ctx->wallet->startRefresh(); + m_wallet->startRefresh(); m_showDeviceError = false; } void MainWindow::onDeviceButtonRequest(quint64 code) { qDebug() << "DeviceButtonRequest, code: " << code; - if (m_ctx->wallet->isTrezor()) { + if (m_wallet->isTrezor()) { switch (code) { case 1: { @@ -1306,41 +1340,41 @@ void MainWindow::onWalletPassphraseNeeded(bool on_device) { "the hardware wallet for better security.", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); if (button == QMessageBox::Yes) { - m_ctx->wallet->onPassphraseEntered("", true, false); + m_wallet->onPassphraseEntered("", true, false); return; } bool ok; QString passphrase = QInputDialog::getText(nullptr, "Wallet Passphrase Needed", "Enter passphrase:", QLineEdit::EchoMode::Password, "", &ok); if (ok) { - m_ctx->wallet->onPassphraseEntered(passphrase, false, false); + m_wallet->onPassphraseEntered(passphrase, false, false); } else { - m_ctx->wallet->onPassphraseEntered(passphrase, false, true); + m_wallet->onPassphraseEntered(passphrase, false, true); } } void MainWindow::updateNetStats() { - if (!m_ctx->wallet || m_ctx->wallet->connectionStatus() == Wallet::ConnectionStatus_Disconnected - || m_ctx->wallet->connectionStatus() == Wallet::ConnectionStatus_Synchronized) + if (!m_wallet || m_wallet->connectionStatus() == Wallet::ConnectionStatus_Disconnected + || m_wallet->connectionStatus() == Wallet::ConnectionStatus_Synchronized) { m_statusLabelNetStats->hide(); return; } m_statusLabelNetStats->show(); - m_statusLabelNetStats->setText(QString("(D: %1)").arg(Utils::formatBytes(m_ctx->wallet->getBytesReceived()))); + m_statusLabelNetStats->setText(QString("(D: %1)").arg(Utils::formatBytes(m_wallet->getBytesReceived()))); } void MainWindow::rescanSpent() { - if (!m_ctx->wallet->rescanSpent()) { - QMessageBox::warning(this, "Rescan spent", m_ctx->wallet->errorString()); + if (!m_wallet->rescanSpent()) { + QMessageBox::warning(this, "Rescan spent", m_wallet->errorString()); } else { QMessageBox::information(this, "Rescan spent", "Successfully rescanned spent outputs."); } } void MainWindow::showBalanceDialog() { - BalanceDialog dialog{this, m_ctx->wallet}; + BalanceDialog dialog{this, m_wallet}; dialog.exec(); } @@ -1420,7 +1454,7 @@ void MainWindow::onInitiateTransaction() { m_constructingTransaction = true; m_txTimer.start(1000); - if (m_ctx->wallet->isHwBacked()) { + if (m_wallet->isHwBacked()) { QString message = "Constructing transaction: action may be required on device."; m_splashDialog->setMessage(message); m_splashDialog->setIcon(QPixmap(":/assets/images/unconfirmed.png")); @@ -1435,7 +1469,7 @@ void MainWindow::onEndTransaction() { m_txTimer.stop(); this->setStatusText(m_statusText); - if (m_ctx->wallet->isHwBacked()) { + if (m_wallet->isHwBacked()) { m_splashDialog->hide(); } } @@ -1456,7 +1490,7 @@ void MainWindow::onSelectedInputsChanged(const QStringList &selectedInputs) { if (numInputs > 0) { quint64 totalAmount = 0; - auto coins = m_ctx->wallet->coins()->coinsFromKeyImage(selectedInputs); + auto coins = m_wallet->coins()->coinsFromKeyImage(selectedInputs); for (const auto coin : coins) { totalAmount += coin->amount(); } @@ -1467,20 +1501,20 @@ void MainWindow::onSelectedInputsChanged(const QStringList &selectedInputs) { } void MainWindow::onExportHistoryCSV(bool checked) { - if (m_ctx->wallet == nullptr) + if (m_wallet == nullptr) return; QString fn = QFileDialog::getSaveFileName(this, "Save CSV file", QDir::homePath(), "CSV (*.csv)"); if (fn.isEmpty()) return; if (!fn.endsWith(".csv")) fn += ".csv"; - m_ctx->wallet->history()->writeCSV(fn); + m_wallet->history()->writeCSV(fn); QMessageBox::information(this, "CSV export", QString("Transaction history exported to %1").arg(fn)); } void MainWindow::onExportContactsCSV(bool checked) { - if (m_ctx->wallet == nullptr) return; - auto *model = m_ctx->wallet->addressBookModel(); + if (m_wallet == nullptr) return; + auto *model = m_wallet->addressBookModel(); if (model->rowCount() <= 0){ QMessageBox::warning(this, "Error", "Addressbook empty"); return; @@ -1540,19 +1574,19 @@ QString MainWindow::getPlatformTag() { } QString MainWindow::getHardwareDevice() { - if (!m_ctx->wallet->isHwBacked()) + if (!m_wallet->isHwBacked()) return ""; - if (m_ctx->wallet->isTrezor()) + if (m_wallet->isTrezor()) return "Trezor"; - if (m_ctx->wallet->isLedger()) + if (m_wallet->isLedger()) return "Ledger"; return "Unknown"; } void MainWindow::updateTitle() { - QString title = QString("%1 (#%2)").arg(this->walletName(), QString::number(m_ctx->wallet->currentSubaddressAccount())); + QString title = QString("%1 (#%2)").arg(this->walletName(), QString::number(m_wallet->currentSubaddressAccount())); - if (m_ctx->wallet->viewOnly()) + if (m_wallet->viewOnly()) title += " [view-only]"; #ifdef HAS_XMRIG if (m_xmrig->isMining()) @@ -1565,13 +1599,13 @@ void MainWindow::updateTitle() { } void MainWindow::donationNag() { - if (m_ctx->networkType != NetworkType::Type::MAINNET) + if (m_wallet->nettype() != NetworkType::Type::MAINNET) return; - if (m_ctx->wallet->viewOnly()) + if (m_wallet->viewOnly()) return; - if (m_ctx->wallet->balanceAll() == 0) + if (m_wallet->balanceAll() == 0) return; auto donationCounter = config()->get(Config::donateBeg).toInt(); @@ -1640,7 +1674,7 @@ bool MainWindow::verifyPassword(bool sensitive) { return false; } - if (!m_ctx->wallet->verifyPassword(passwordDialog.password)) { + if (!m_wallet->verifyPassword(passwordDialog.password)) { incorrectPassword = true; continue; } @@ -1707,7 +1741,7 @@ void MainWindow::unlockWallet(const QString &password) { return; } - if (!m_ctx->wallet->verifyPassword(password)) { + if (!m_wallet->verifyPassword(password)) { m_walletUnlockWidget->incorrectPassword(); return; } @@ -1742,4 +1776,6 @@ void MainWindow::toggleSearchbar(bool visible) { m_coinsWidget->focusSearchbar(); } -MainWindow::~MainWindow() = default; \ No newline at end of file +MainWindow::~MainWindow() { + qDebug() << "~MainWindow"; +}; \ No newline at end of file diff --git a/src/MainWindow.h b/src/MainWindow.h index 64c0ae2..4b27075 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -8,7 +8,6 @@ #include #include -#include "appcontext.h" #include "components.h" #include "CalcWindow.h" #include "SettingsDialog.h" @@ -30,6 +29,7 @@ #include "model/CoinsProxyModel.h" #include "utils/networking.h" #include "utils/config.h" +#include "utils/daemonrpc.h" #include "utils/EventFilter.h" #include "utils/Updater.h" #include "widgets/CCSWidget.h" @@ -192,6 +192,7 @@ private slots: void onWebsocketStatusChanged(bool enabled); void showUpdateNotification(); void onProxySettingsChanged(); + void onOfflineMode(bool offline); private: friend WindowManager; @@ -235,7 +236,9 @@ private: QScopedPointer ui; WindowManager *m_windowManager; - QSharedPointer m_ctx; + Wallet *m_wallet = nullptr; + Nodes *m_nodes; + DaemonRpc *m_rpc; CalcWindow *m_windowCalc = nullptr; SplashDialog *m_splashDialog = nullptr; diff --git a/src/ReceiveWidget.cpp b/src/ReceiveWidget.cpp index 6d9a058..0bc180d 100644 --- a/src/ReceiveWidget.cpp +++ b/src/ReceiveWidget.cpp @@ -12,15 +12,15 @@ #include "model/ModelUtils.h" #include "utils/Icons.h" -ReceiveWidget::ReceiveWidget(QSharedPointer ctx, QWidget *parent) +ReceiveWidget::ReceiveWidget(Wallet *wallet, QWidget *parent) : QWidget(parent) , ui(new Ui::ReceiveWidget) - , m_ctx(std::move(ctx)) + , m_wallet(wallet) { ui->setupUi(this); - m_model = m_ctx->wallet->subaddressModel(); - m_proxyModel = new SubaddressProxyModel(this, m_ctx->wallet->subaddress()); + m_model = m_wallet->subaddressModel(); + m_proxyModel = new SubaddressProxyModel(this, m_wallet->subaddress()); m_proxyModel->setSourceModel(m_model); m_proxyModel->setHiddenAddresses(this->getHiddenAddresses()); @@ -112,7 +112,7 @@ void ReceiveWidget::showContextMenu(const QPoint &point) { menu->addAction("Hide address", this, &ReceiveWidget::hideAddress); } - if (m_ctx->wallet->isHwBacked()) { + if (m_wallet->isHwBacked()) { menu->addAction("Show on device", this, &ReceiveWidget::showOnDevice); } @@ -127,7 +127,7 @@ void ReceiveWidget::createPaymentRequest() { QString address = index.model()->data(index.siblingAtColumn(SubaddressModel::Address), Qt::UserRole).toString(); - PaymentRequestDialog dialog{this, m_ctx, address}; + PaymentRequestDialog dialog{this, m_wallet, address}; dialog.exec(); } @@ -189,13 +189,13 @@ void ReceiveWidget::showAddress() void ReceiveWidget::showOnDevice() { Monero::SubaddressRow* row = this->currentEntry(); if (!row) return; - m_ctx->wallet->deviceShowAddressAsync(m_ctx->wallet->currentSubaddressAccount(), row->getRowId(), ""); + m_wallet->deviceShowAddressAsync(m_wallet->currentSubaddressAccount(), row->getRowId(), ""); } void ReceiveWidget::generateSubaddress() { - bool r = m_ctx->wallet->subaddress()->addRow(m_ctx->wallet->currentSubaddressAccount(), ""); + bool r = m_wallet->subaddress()->addRow(m_wallet->currentSubaddressAccount(), ""); if (!r) { - QMessageBox::warning(this, "Warning", QString("Failed to generate subaddress:\n\n%1").arg(m_ctx->wallet->subaddress()->errorString())); + QMessageBox::warning(this, "Warning", QString("Failed to generate subaddress:\n\n%1").arg(m_wallet->subaddress()->errorString())); } } @@ -229,7 +229,7 @@ void ReceiveWidget::showQrCodeDialog() { } QStringList ReceiveWidget::getHiddenAddresses() { - QString data = m_ctx->wallet->getCacheAttribute("feather.hiddenaddresses"); + QString data = m_wallet->getCacheAttribute("feather.hiddenaddresses"); return data.split(","); } @@ -239,14 +239,14 @@ void ReceiveWidget::addHiddenAddress(const QString& address) { hiddenAddresses.append(address); } QString data = hiddenAddresses.join(","); - m_ctx->wallet->setCacheAttribute("feather.hiddenaddresses", data); + m_wallet->setCacheAttribute("feather.hiddenaddresses", data); } void ReceiveWidget::removeHiddenAddress(const QString &address) { QStringList hiddenAddresses = this->getHiddenAddresses(); hiddenAddresses.removeAll(address); QString data = hiddenAddresses.join(","); - m_ctx->wallet->setCacheAttribute("feather.hiddenaddresses", data); + m_wallet->setCacheAttribute("feather.hiddenaddresses", data); } Monero::SubaddressRow* ReceiveWidget::currentEntry() { diff --git a/src/ReceiveWidget.h b/src/ReceiveWidget.h index 969863d..aefed1a 100644 --- a/src/ReceiveWidget.h +++ b/src/ReceiveWidget.h @@ -8,8 +8,8 @@ #include #include -#include "appcontext.h" #include "libwalletqt/Subaddress.h" +#include "libwalletqt/Wallet.h" #include "model/SubaddressProxyModel.h" #include "model/SubaddressModel.h" #include "qrcode/QrCode.h" @@ -23,7 +23,7 @@ class ReceiveWidget : public QWidget Q_OBJECT public: - explicit ReceiveWidget(QSharedPointer ctx, QWidget *parent = nullptr); + explicit ReceiveWidget(Wallet *wallet, QWidget *parent = nullptr); ~ReceiveWidget() override; void setSearchbarVisible(bool visible); @@ -53,7 +53,7 @@ private slots: private: QScopedPointer ui; - QSharedPointer m_ctx; + Wallet *m_wallet; QMenu *m_headerMenu; QAction *m_showFullAddressesAction; QAction *m_showUsedAddressesAction; diff --git a/src/SendWidget.cpp b/src/SendWidget.cpp index 205db75..6554e51 100644 --- a/src/SendWidget.cpp +++ b/src/SendWidget.cpp @@ -9,7 +9,9 @@ #include "ColorScheme.h" #include "constants.h" #include "utils/AppData.h" +#include "utils/config.h" #include "Icons.h" +#include "libwalletqt/WalletManager.h" #if defined(WITH_SCANNER) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #include "qrcode_scanner/QrCodeScanDialog.h" @@ -19,10 +21,10 @@ #include #endif -SendWidget::SendWidget(QSharedPointer ctx, QWidget *parent) +SendWidget::SendWidget(Wallet *wallet, QWidget *parent) : QWidget(parent) , ui(new Ui::SendWidget) - , m_ctx(std::move(ctx)) + , m_wallet(wallet) { ui->setupUi(this); @@ -32,8 +34,8 @@ SendWidget::SendWidget(QSharedPointer ctx, QWidget *parent) QValidator *validator = new QRegularExpressionValidator(rx, this); ui->lineAmount->setValidator(validator); - connect(m_ctx.get(), &AppContext::initiateTransaction, this, &SendWidget::onInitiateTransaction); - connect(m_ctx.get(), &AppContext::endTransaction, this, &SendWidget::onEndTransaction); + connect(m_wallet, &Wallet::initiateTransaction, this, &SendWidget::onInitiateTransaction); + connect(m_wallet, &Wallet::endTransaction, this, &SendWidget::onEndTransaction); connect(WalletManager::instance(), &WalletManager::openAliasResolved, this, &SendWidget::onOpenAliasResolved); @@ -143,14 +145,14 @@ void SendWidget::scanClicked() { } void SendWidget::sendClicked() { - if (!m_ctx->wallet->isConnected()) { + if (!m_wallet->isConnected()) { QMessageBox::warning(this, "Error", "Unable to create transaction:\n\n" "Wallet is not connected to a node.\n" "Go to File -> Settings -> Node to manually connect to a node."); return; } - if (!m_ctx->wallet->isSynchronized()) { + if (!m_wallet->isSynchronized()) { QMessageBox::warning(this, "Error", "Wallet is not synchronized, unable to create transaction.\n\n" "Wait for synchronization to complete."); return; @@ -189,7 +191,7 @@ void SendWidget::sendClicked() { amounts.push_back(output.amount); } - m_ctx->onCreateTransactionMultiDest(addresses, amounts, description); + m_wallet->createTransactionMultiDest(addresses, amounts, description); return; } @@ -207,7 +209,7 @@ void SendWidget::sendClicked() { amount = WalletManager::amountFromDouble(this->conversionAmount()); } - m_ctx->onCreateTransaction(recipient, amount, description, sendAll); + m_wallet->createTransaction(recipient, amount, description, sendAll); } void SendWidget::aliasClicked() { @@ -347,7 +349,7 @@ void SendWidget::setWebsocketEnabled(bool enabled) { void SendWidget::onDataPasted(const QString &data) { if (!data.isEmpty()) { - QVariantMap uriData = m_ctx->wallet->parse_uri_to_object(data); + QVariantMap uriData = m_wallet->parse_uri_to_object(data); if (!uriData.contains("error")) { ui->lineAddress->setText(uriData.value("address").toString()); ui->lineDescription->setText(uriData.value("tx_description").toString()); diff --git a/src/SendWidget.h b/src/SendWidget.h index 5369ac7..ca13759 100644 --- a/src/SendWidget.h +++ b/src/SendWidget.h @@ -6,7 +6,7 @@ #include -#include "appcontext.h" +#include "libwalletqt/Wallet.h" #include "widgets/CCSWidget.h" namespace Ui { @@ -18,7 +18,7 @@ class SendWidget : public QWidget Q_OBJECT public: - explicit SendWidget(QSharedPointer ctx, QWidget *parent = nullptr); + explicit SendWidget(Wallet *wallet, QWidget *parent = nullptr); void fill(const QString &address, const QString &description, double amount = 0); void fill(double amount); void clearFields(); @@ -57,7 +57,7 @@ private: double conversionAmount(); QScopedPointer ui; - QSharedPointer m_ctx; + Wallet *m_wallet; bool m_sendDisabled = false; }; diff --git a/src/SettingsDialog.cpp b/src/SettingsDialog.cpp index e5fe82e..d97635c 100644 --- a/src/SettingsDialog.cpp +++ b/src/SettingsDialog.cpp @@ -9,14 +9,16 @@ #include #include +#include "libwalletqt/WalletManager.h" +#include "utils/AppData.h" #include "utils/Icons.h" #include "utils/WebsocketNotifier.h" #include "widgets/NetworkProxyWidget.h" -Settings::Settings(QSharedPointer ctx, QWidget *parent) +Settings::Settings(Nodes *nodes, QWidget *parent) : QDialog(parent) , ui(new Ui::Settings) - , m_ctx(std::move(ctx)) + , m_nodes(nodes) { ui->setupUi(this); @@ -150,12 +152,12 @@ void Settings::setupAppearanceTab() { void Settings::setupNetworkTab() { // Node - if (m_ctx) { - ui->nodeWidget->setupUI(m_ctx->nodes); - connect(ui->nodeWidget, &NodeWidget::nodeSourceChanged, m_ctx->nodes, &Nodes::onNodeSourceChanged); - connect(ui->nodeWidget, &NodeWidget::connectToNode, m_ctx->nodes, QOverload::of(&Nodes::connectToNode)); + if (m_nodes) { + ui->nodeWidget->setupUI(m_nodes); + connect(ui->nodeWidget, &NodeWidget::nodeSourceChanged, m_nodes, &Nodes::onNodeSourceChanged); + connect(ui->nodeWidget, &NodeWidget::connectToNode, m_nodes, QOverload::of(&Nodes::connectToNode)); } else { - m_nodes = new Nodes(this); + m_nodes = new Nodes(this, nullptr); ui->nodeWidget->setupUI(m_nodes); ui->nodeWidget->setCanConnect(false); } diff --git a/src/SettingsDialog.h b/src/SettingsDialog.h index b8b1f91..77809fb 100644 --- a/src/SettingsDialog.h +++ b/src/SettingsDialog.h @@ -9,7 +9,6 @@ #include #include -#include "appcontext.h" #include "widgets/NodeWidget.h" namespace Ui { @@ -21,7 +20,7 @@ class Settings : public QDialog Q_OBJECT public: - explicit Settings(QSharedPointer ctx, QWidget *parent = nullptr); + explicit Settings(Nodes *nodes, QWidget *parent = nullptr); ~Settings() override; void showNetworkProxyTab(); @@ -67,7 +66,6 @@ private: void enableWebsocket(bool enabled); QScopedPointer ui; - QSharedPointer m_ctx; Nodes *m_nodes = nullptr; QStringList m_themes{"Native", "QDarkStyle", "Breeze/Dark", "Breeze/Light"}; diff --git a/src/WindowManager.cpp b/src/WindowManager.cpp index bce0f5c..aea1172 100644 --- a/src/WindowManager.cpp +++ b/src/WindowManager.cpp @@ -13,11 +13,13 @@ #include "utils/Icons.h" #include "utils/NetworkManager.h" #include "utils/os/tails.h" +#include "utils/os/whonix.h" #include "utils/TorManager.h" #include "utils/WebsocketNotifier.h" -WindowManager::WindowManager(EventFilter *eventFilter) - : eventFilter(eventFilter) +WindowManager::WindowManager(QObject *parent, EventFilter *eventFilter) + : QObject(parent) + , eventFilter(eventFilter) { m_walletManager = WalletManager::instance(); m_splashDialog = new SplashDialog; @@ -83,12 +85,15 @@ void WindowManager::close() { } void WindowManager::closeWindow(MainWindow *window) { + qDebug() << "closing Window"; m_windows.removeOne(window); - // Move Wallet to a different thread for cleanup so it doesn't block GUI thread - window->m_ctx->wallet->moveToThread(m_cleanupThread); + // Move Wallet to a different thread for cleanup, so it doesn't block GUI thread + window->m_wallet->moveToThread(m_cleanupThread); m_cleanupThread->start(); - window->m_ctx->wallet->deleteLater(); + window->m_wallet->deleteLater(); + + window->deleteLater(); } void WindowManager::restartApplication(const QString &binaryFilename) { @@ -138,8 +143,8 @@ void WindowManager::raise() { // ######################## SETTINGS ######################## -void WindowManager::showSettings(QSharedPointer ctx, QWidget *parent, bool showProxyTab) { - Settings settings{ctx, parent}; +void WindowManager::showSettings(Nodes *nodes, QWidget *parent, bool showProxyTab) { + Settings settings{nodes, parent}; connect(&settings, &Settings::preferredFiatCurrencyChanged, [this]{ for (const auto &window : m_windows) { @@ -301,6 +306,7 @@ void WindowManager::tryCreateWallet(Seed seed, const QString &path, const QStrin wallet->setCacheAttribute("feather.seed", seed.mnemonic.join(" ")); wallet->setCacheAttribute("feather.seedoffset", seedOffset); + wallet->setNewWallet(); this->onWalletOpened(wallet); } @@ -359,6 +365,7 @@ void WindowManager::onWalletCreated(Wallet *wallet) { // Currently only called when a wallet is created from device. auto state = wallet->status(); if (state != Wallet::Status_Ok) { + wallet->setNewWallet(); qDebug() << Q_FUNC_INFO << QString("Wallet open error: %1").arg(wallet->errorString()); this->displayWalletErrorMessage(wallet->errorString()); m_splashDialog->hide(); diff --git a/src/WindowManager.h b/src/WindowManager.h index 9c5fbd2..d8dc074 100644 --- a/src/WindowManager.h +++ b/src/WindowManager.h @@ -10,6 +10,7 @@ #include "libwalletqt/WalletManager.h" #include "libwalletqt/Wallet.h" #include "MainWindow.h" +#include "utils/nodes.h" #include "wizard/WalletWizard.h" class MainWindow; @@ -17,7 +18,7 @@ class WindowManager : public QObject { Q_OBJECT public: - explicit WindowManager(EventFilter *eventFilter); + explicit WindowManager(QObject *parent, EventFilter *eventFilter); ~WindowManager() override; void wizardOpenWallet(); @@ -27,7 +28,7 @@ public: void restartApplication(const QString &binaryFilename); void raise(); - void showSettings(QSharedPointer ctx, QWidget *parent, bool showProxyTab = false); + void showSettings(Nodes *nodes, QWidget *parent, bool showProxyTab = false); EventFilter *eventFilter; diff --git a/src/appcontext.cpp b/src/appcontext.cpp deleted file mode 100644 index efad102..0000000 --- a/src/appcontext.cpp +++ /dev/null @@ -1,343 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// SPDX-FileCopyrightText: 2020-2023 The Monero Project - -#include - -#include "appcontext.h" -#include "constants.h" - -// libwalletqt -#include "libwalletqt/TransactionHistory.h" -#include "libwalletqt/Subaddress.h" -#include "libwalletqt/Coins.h" -#include "model/TransactionHistoryModel.h" -#include "model/SubaddressModel.h" -#include "utils/NetworkManager.h" -#include "utils/WebsocketClient.h" -#include "utils/WebsocketNotifier.h" - -// This class serves as a business logic layer between MainWindow and libwalletqt. -// This way we don't clutter the GUI with wallet logic, -// and keep libwalletqt (mostly) clean of Feather specific implementation details - -AppContext::AppContext(Wallet *wallet) - : wallet(wallet) - , nodes(new Nodes(this)) - , networkType(constants::networkType) - , m_rpc(new DaemonRpc{this, ""}) -{ - connect(this->wallet, &Wallet::moneySpent, this, &AppContext::onMoneySpent); - connect(this->wallet, &Wallet::moneyReceived, this, &AppContext::onMoneyReceived); - connect(this->wallet, &Wallet::unconfirmedMoneyReceived, this, &AppContext::onUnconfirmedMoneyReceived); - connect(this->wallet, &Wallet::newBlock, this, &AppContext::onWalletNewBlock); - connect(this->wallet, &Wallet::updated, this, &AppContext::onWalletUpdate); - connect(this->wallet, &Wallet::refreshed, this, &AppContext::onWalletRefreshed); - connect(this->wallet, &Wallet::transactionCommitted, this, &AppContext::onTransactionCommitted); - connect(this->wallet, &Wallet::heightRefreshed, this, &AppContext::onHeightRefreshed); - connect(this->wallet, &Wallet::transactionCreated, this, &AppContext::onTransactionCreated); - connect(this->wallet, &Wallet::deviceError, this, &AppContext::onDeviceError); - connect(this->wallet, &Wallet::deviceButtonRequest, this, &AppContext::onDeviceButtonRequest); - connect(this->wallet, &Wallet::deviceButtonPressed, this, &AppContext::onDeviceButtonPressed); - connect(this->wallet, &Wallet::currentSubaddressAccountChanged, [this]{ - this->updateBalance(); - }); - - connect(this, &AppContext::createTransactionError, this, &AppContext::onCreateTransactionError); - - nodes->setContext(this); - - // Store the wallet every 2 minutes - m_storeTimer.start(2 * 60 * 1000); - connect(&m_storeTimer, &QTimer::timeout, [this](){ - this->storeWallet(); - }); - - this->updateBalance(); - - connect(this->wallet->history(), &TransactionHistory::txNoteChanged, [this]{ - this->wallet->history()->refresh(this->wallet->currentSubaddressAccount()); - }); -} - -// ################## Transaction creation ################## - -void AppContext::onCreateTransaction(const QString &address, quint64 amount, const QString &description, bool all) { - this->tmpTxDescription = description; - - if (!all && amount == 0) { - emit createTransactionError("Cannot send nothing"); - return; - } - - quint64 unlocked_balance = this->wallet->unlockedBalance(); - if (!all && amount > unlocked_balance) { - emit createTransactionError(QString("Not enough money to spend.\n\n" - "Spendable balance: %1").arg(WalletManager::displayAmount(unlocked_balance))); - return; - } else if (unlocked_balance == 0) { - emit createTransactionError("No money to spend"); - return; - } - - qInfo() << "Creating transaction"; - if (all) - this->wallet->createTransactionAllAsync(address, "", constants::mixin, this->tx_priority, m_selectedInputs); - else - this->wallet->createTransactionAsync(address, "", amount, constants::mixin, this->tx_priority, m_selectedInputs); - - emit initiateTransaction(); -} - -void AppContext::onCreateTransactionMultiDest(const QVector &addresses, const QVector &amounts, const QString &description) { - this->tmpTxDescription = description; - - quint64 total_amount = 0; - for (auto &amount : amounts) { - total_amount += amount; - } - - auto unlocked_balance = this->wallet->unlockedBalance(); - if (total_amount > unlocked_balance) { - emit createTransactionError("Not enough money to spend"); - } - - qInfo() << "Creating transaction"; - this->wallet->createTransactionMultiDestAsync(addresses, amounts, this->tx_priority, m_selectedInputs); - - emit initiateTransaction(); -} - -void AppContext::onSweepOutputs(const QVector &keyImages, QString address, bool churn, int outputs) { - if (churn) { - address = this->wallet->address(0, 0); - } - - qInfo() << "Creating transaction"; - this->wallet->createTransactionSelectedAsync(keyImages, address, outputs, this->tx_priority); - - emit initiateTransaction(); -} - -void AppContext::onCreateTransactionError(const QString &msg) { - this->tmpTxDescription = ""; - emit endTransaction(); -} - -void AppContext::onCancelTransaction(PendingTransaction *tx, const QVector &address) { - // tx cancelled by user - emit createTransactionCancelled(address, tx->amount()); - this->wallet->disposeTransaction(tx); -} - -void AppContext::commitTransaction(PendingTransaction *tx, const QString &description) { - // Clear list of selected transfers - this->setSelectedInputs({}); - - // Nodes - even well-connected, properly configured ones - consistently fail to relay transactions - // To mitigate transactions failing we just send the transaction to every node we know about over Tor - if (config()->get(Config::multiBroadcast).toBool()) { - this->onMultiBroadcast(tx); - } - - this->wallet->commitTransactionAsync(tx, description); -} - -void AppContext::onMultiBroadcast(PendingTransaction *tx) { - quint64 count = tx->txCount(); - for (quint64 i = 0; i < count; i++) { - QString txData = tx->signedTxToHex(i); - - for (const auto& node: this->nodes->nodes()) { - QString address = node.toURL(); - qDebug() << QString("Relaying %1 to: %2").arg(tx->txid()[i], address); - m_rpc->setDaemonAddress(address); - m_rpc->sendRawTransaction(txData); - } - } -} - -void AppContext::addCacheTransaction(const QString &txid, const QString &txHex) const { - this->wallet->setCacheAttribute(QString("tx:%1").arg(txid), txHex); -} - -QString AppContext::getCacheTransaction(const QString &txid) const { - QString txHex = this->wallet->getCacheAttribute(QString("tx:%1").arg(txid)); - return txHex; -} - -// ################## Device ################## - -void AppContext::onDeviceButtonRequest(quint64 code) { - emit deviceButtonRequest(code); -} - -void AppContext::onDeviceButtonPressed() { - emit deviceButtonPressed(); -} - -void AppContext::onDeviceError(const QString &message) { - qCritical() << "Device error: " << message; - emit deviceError(message); -} - -// ################## Misc ################## - -void AppContext::setSelectedInputs(const QStringList &selectedInputs) { - m_selectedInputs = selectedInputs; - emit selectedInputsChanged(selectedInputs); -} - -void AppContext::onProxySettingsChanged() { - if (Utils::isTorsocks()) { - return; - } - - this->nodes->connectToNode(); -} - -void AppContext::stopTimers() { - m_storeTimer.stop(); -} - -// ########################################## LIBWALLET QT SIGNALS #################################################### - -void AppContext::onMoneySpent(const QString &txId, quint64 amount) { - // Outgoing tx included in a block - qDebug() << Q_FUNC_INFO << txId << " " << WalletManager::displayAmount(amount); -} - -void AppContext::onMoneyReceived(const QString &txId, quint64 amount) { - // Incoming tx included in a block. - qDebug() << Q_FUNC_INFO << txId << " " << WalletManager::displayAmount(amount); -} - -void AppContext::onUnconfirmedMoneyReceived(const QString &txId, quint64 amount) { - // Incoming tx in pool - qDebug() << Q_FUNC_INFO << txId << " " << WalletManager::displayAmount(amount); - - if (this->wallet->synchronized()) { - auto notify = QString("%1 XMR (pending)").arg(WalletManager::displayAmount(amount, false)); - Utils::desktopNotify("Payment received", notify, 5000); - } -} - -void AppContext::onWalletUpdate() { - if (this->wallet->synchronized()) { - this->refreshModels(); - this->storeWallet(); - } - - this->updateBalance(); -} - -void AppContext::onWalletRefreshed(bool success, const QString &message) { - if (!success) { - // Something went wrong during refresh, in some cases we need to notify the user - qCritical() << "Exception during refresh: " << message; // Can't use ->errorString() here, other SLOT might snipe it first - return; - } - - if (!this->refreshed) { - refreshModels(); - this->refreshed = true; - emit walletRefreshed(); - // store wallet immediately upon finishing synchronization - this->wallet->store(); - } -} - -void AppContext::onWalletNewBlock(quint64 blockheight, quint64 targetHeight) { - // Called whenever a new block gets scanned by the wallet - this->syncStatusUpdated(blockheight, targetHeight); - - if (this->wallet->isSynchronized()) { - this->wallet->coins()->refreshUnlocked(); - this->wallet->history()->refresh(this->wallet->currentSubaddressAccount()); - // Todo: only refresh tx confirmations - } -} - -void AppContext::onHeightRefreshed(quint64 walletHeight, quint64 daemonHeight, quint64 targetHeight) { - if (this->wallet->connectionStatus() == Wallet::ConnectionStatus_Disconnected) - return; - - if (daemonHeight < targetHeight) { - emit blockchainSync(daemonHeight, targetHeight); - } - else { - this->syncStatusUpdated(walletHeight, daemonHeight); - } -} - -void AppContext::onTransactionCreated(PendingTransaction *tx, const QVector &address) { - qDebug() << Q_FUNC_INFO; - - for (auto &addr : address) { - if (addr == constants::donationAddress) { - this->donationSending = true; - } - } - - // Let UI know that the transaction was constructed - emit endTransaction(); - - // tx created, but not sent yet. ask user to verify first. - emit createTransactionSuccess(tx, address); -} - -void AppContext::onTransactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid){ - // Store wallet immediately so we don't risk losing tx key if wallet crashes - this->wallet->store(); - - this->wallet->history()->refresh(this->wallet->currentSubaddressAccount()); - this->wallet->coins()->refresh(this->wallet->currentSubaddressAccount()); - - this->updateBalance(); - - // this tx was a donation to Feather, stop our nagging - if (this->donationSending) { - this->donationSending = false; - config()->set(Config::donateBeg, -1); - } - - emit transactionCommitted(status, tx, txid); -} - -void AppContext::storeWallet() { - // Do not store a synchronizing wallet: store() is NOT thread safe and may crash the wallet - if (!this->wallet->isSynchronized()) - return; - - qDebug() << "Storing wallet"; - this->wallet->store(); -} - -void AppContext::updateBalance() { - quint64 balance = this->wallet->balance(); - quint64 spendable = this->wallet->unlockedBalance(); - - emit balanceUpdated(balance, spendable); -} - -void AppContext::syncStatusUpdated(quint64 height, quint64 target) { - if (height < (target - 1)) { - emit refreshSync(height, target); - } - else { - this->updateBalance(); - emit synchronized(); - } -} - -void AppContext::refreshModels() { - this->wallet->history()->refresh(this->wallet->currentSubaddressAccount()); - this->wallet->coins()->refresh(this->wallet->currentSubaddressAccount()); - bool r = this->wallet->subaddress()->refresh(this->wallet->currentSubaddressAccount()); - - if (!r) { - // This should only happen if wallet keys got corrupted or were tampered with - // The list of subaddresses is wiped to prevent loss of funds - // Notify MainWindow to display an error message - emit keysCorrupted(); - } -} diff --git a/src/appcontext.h b/src/appcontext.h deleted file mode 100644 index 4b11983..0000000 --- a/src/appcontext.h +++ /dev/null @@ -1,103 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// SPDX-FileCopyrightText: 2020-2023 The Monero Project - -#ifndef FEATHER_APPCONTEXT_H -#define FEATHER_APPCONTEXT_H - -#include -#include - -#include "utils/os/whonix.h" -#include "utils/networking.h" -#include "utils/Seed.h" -#include "utils/daemonrpc.h" -#include "utils/RestoreHeightLookup.h" -#include "utils/nodes.h" - -#include "libwalletqt/WalletManager.h" -#include "PendingTransaction.h" - -class AppContext : public QObject -{ -Q_OBJECT - -public: - explicit AppContext(Wallet *wallet); - - Wallet *wallet; - Nodes *nodes; - - bool donationSending = false; - - QString tmpTxDescription; // TODO: remove the need for this var - - NetworkType::Type networkType; - PendingTransaction::Priority tx_priority = PendingTransaction::Priority::Priority_Low; - - // libwalletqt - bool refreshed = false; - - void commitTransaction(PendingTransaction *tx, const QString &description=""); - void syncStatusUpdated(quint64 height, quint64 target); - void updateBalance(); - void refreshModels(); - - void storeWallet(); - - void stopTimers(); - - void addCacheTransaction(const QString &txid, const QString &txHex) const; - QString getCacheTransaction(const QString &txid) const; - - void setSelectedInputs(const QStringList &selectedInputs); - -public slots: - void onCreateTransaction(const QString &address, quint64 amount, const QString &description, bool all); - void onCreateTransactionMultiDest(const QVector &addresses, const QVector &amounts, const QString &description); - void onCancelTransaction(PendingTransaction *tx, const QVector &address); - void onSweepOutputs(const QVector &keyImages, QString address, bool churn, int outputs); - void onCreateTransactionError(const QString &msg); - void onMultiBroadcast(PendingTransaction *tx); - void onDeviceButtonRequest(quint64 code); - void onDeviceButtonPressed(); - void onDeviceError(const QString &message); - - void onProxySettingsChanged(); // should not be here - -private slots: - void onMoneySpent(const QString &txId, quint64 amount); - void onMoneyReceived(const QString &txId, quint64 amount); - void onUnconfirmedMoneyReceived(const QString &txId, quint64 amount); - void onWalletUpdate(); - void onWalletRefreshed(bool success, const QString &message); - - void onWalletNewBlock(quint64 blockheight, quint64 targetHeight); - void onHeightRefreshed(quint64 walletHeight, quint64 daemonHeight, quint64 targetHeight); - void onTransactionCreated(PendingTransaction *tx, const QVector &address); - void onTransactionCommitted(bool status, PendingTransaction *t, const QStringList& txid); - -signals: - void balanceUpdated(quint64 balance, quint64 spendable); - void blockchainSync(int height, int target); - void refreshSync(int height, int target); - void synchronized(); - void walletRefreshed(); - void transactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid); - void createTransactionError(QString message); - void createTransactionCancelled(const QVector &address, quint64 amount); - void createTransactionSuccess(PendingTransaction *tx, const QVector &address); - void initiateTransaction(); - void endTransaction(); - void deviceButtonRequest(quint64 code); - void deviceButtonPressed(); - void deviceError(const QString &message); - void keysCorrupted(); - void selectedInputsChanged(const QStringList &selectedInputs); - -private: - DaemonRpc *m_rpc; - QTimer m_storeTimer; - QStringList m_selectedInputs; -}; - -#endif //FEATHER_APPCONTEXT_H diff --git a/src/assets/tor2/tor b/src/assets/tor2/tor new file mode 100755 index 0000000..23162a8 Binary files /dev/null and b/src/assets/tor2/tor differ diff --git a/src/dialog/AccountSwitcherDialog.cpp b/src/dialog/AccountSwitcherDialog.cpp index ad24c24..68fae91 100644 --- a/src/dialog/AccountSwitcherDialog.cpp +++ b/src/dialog/AccountSwitcherDialog.cpp @@ -7,23 +7,25 @@ #include #include "libwalletqt/SubaddressAccount.h" +#include "libwalletqt/WalletManager.h" #include "model/ModelUtils.h" #include "utils/Icons.h" +#include "utils/Utils.h" -AccountSwitcherDialog::AccountSwitcherDialog(QSharedPointer ctx, QWidget *parent) +AccountSwitcherDialog::AccountSwitcherDialog(Wallet *wallet, QWidget *parent) : WindowModalDialog(parent) , ui(new Ui::AccountSwitcherDialog) - , m_ctx(std::move(ctx)) - , m_model(m_ctx->wallet->subaddressAccountModel()) + , m_wallet(wallet) + , m_model(wallet->subaddressAccountModel()) , m_proxyModel(new SubaddressAccountProxyModel(this)) { ui->setupUi(this); - m_ctx->wallet->subaddressAccount()->refresh(); + m_wallet->subaddressAccount()->refresh(); m_proxyModel->setSourceModel(m_model); ui->label_totalBalance->setFont(ModelUtils::getMonospaceFont()); - ui->label_totalBalance->setText(WalletManager::displayAmount(m_ctx->wallet->balanceAll())); + ui->label_totalBalance->setText(WalletManager::displayAmount(m_wallet->balanceAll())); this->setWindowModality(Qt::WindowModal); @@ -43,12 +45,12 @@ AccountSwitcherDialog::AccountSwitcherDialog(QSharedPointer ctx, QWi connect(ui->accounts, &QTreeView::customContextMenuRequested, this, &AccountSwitcherDialog::showContextMenu); connect(ui->btn_newAccount, &QPushButton::clicked, [this]{ - m_ctx->wallet->addSubaddressAccount("New account"); - m_ctx->wallet->subaddressAccount()->refresh(); + m_wallet->addSubaddressAccount("New account"); + m_wallet->subaddressAccount()->refresh(); }); - connect(m_ctx->wallet, &Wallet::currentSubaddressAccountChanged, this, &AccountSwitcherDialog::updateSelection); - connect(m_ctx->wallet->subaddressAccount(), &SubaddressAccount::refreshFinished, this, &AccountSwitcherDialog::updateSelection); + connect(m_wallet, &Wallet::currentSubaddressAccountChanged, this, &AccountSwitcherDialog::updateSelection); + connect(m_wallet->subaddressAccount(), &SubaddressAccount::refreshFinished, this, &AccountSwitcherDialog::updateSelection); this->updateSelection(); } @@ -59,7 +61,7 @@ void AccountSwitcherDialog::switchAccount() { return; } - m_ctx->wallet->switchSubaddressAccount(row->getRowId()); + m_wallet->switchSubaddressAccount(row->getRowId()); } void AccountSwitcherDialog::copyLabel() { @@ -81,13 +83,13 @@ void AccountSwitcherDialog::copyBalance() { } void AccountSwitcherDialog::editLabel() { - QModelIndex index = ui->accounts->currentIndex().siblingAtColumn(m_ctx->wallet->subaddressAccountModel()->Column::Label); + QModelIndex index = ui->accounts->currentIndex().siblingAtColumn(m_wallet->subaddressAccountModel()->Column::Label); ui->accounts->setCurrentIndex(index); ui->accounts->edit(index); } void AccountSwitcherDialog::updateSelection() { - QModelIndex index = m_model->index(m_ctx->wallet->currentSubaddressAccount(), 0); + QModelIndex index = m_model->index(m_wallet->currentSubaddressAccount(), 0); ui->accounts->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); } @@ -108,7 +110,7 @@ void AccountSwitcherDialog::showContextMenu(const QPoint &point) { Monero::SubaddressAccountRow* AccountSwitcherDialog::currentEntry() { QModelIndex index = m_proxyModel->mapToSource(ui->accounts->currentIndex()); - return m_ctx->wallet->subaddressAccountModel()->entryFromIndex(index); + return m_wallet->subaddressAccountModel()->entryFromIndex(index); } AccountSwitcherDialog::~AccountSwitcherDialog() = default; diff --git a/src/dialog/AccountSwitcherDialog.h b/src/dialog/AccountSwitcherDialog.h index 5fea3de..329de74 100644 --- a/src/dialog/AccountSwitcherDialog.h +++ b/src/dialog/AccountSwitcherDialog.h @@ -6,7 +6,7 @@ #include -#include "appcontext.h" +#include "libwalletqt/Wallet.h" #include "components.h" #include "model/SubaddressAccountModel.h" @@ -19,7 +19,7 @@ class AccountSwitcherDialog : public WindowModalDialog Q_OBJECT public: - explicit AccountSwitcherDialog(QSharedPointer ctx, QWidget *parent = nullptr); + explicit AccountSwitcherDialog(Wallet *wallet, QWidget *parent = nullptr); ~AccountSwitcherDialog() override; private slots: @@ -35,7 +35,7 @@ private: Monero::SubaddressAccountRow* currentEntry(); QScopedPointer ui; - QSharedPointer m_ctx; + Wallet *m_wallet; SubaddressAccountModel *m_model; SubaddressAccountProxyModel *m_proxyModel; }; diff --git a/src/dialog/DebugInfoDialog.cpp b/src/dialog/DebugInfoDialog.cpp index 915b545..05e8382 100644 --- a/src/dialog/DebugInfoDialog.cpp +++ b/src/dialog/DebugInfoDialog.cpp @@ -5,15 +5,18 @@ #include "ui_DebugInfoDialog.h" #include "config-feather.h" +#include "utils/AppData.h" #include "utils/os/tails.h" +#include "utils/os/whonix.h" #include "utils/TorManager.h" #include "utils/WebsocketClient.h" #include "utils/WebsocketNotifier.h" -DebugInfoDialog::DebugInfoDialog(QSharedPointer ctx, QWidget *parent) +DebugInfoDialog::DebugInfoDialog(Wallet *wallet, Nodes *nodes, QWidget *parent) : WindowModalDialog(parent) , ui(new Ui::DebugInfoDialog) - , m_ctx(std::move(ctx)) + , m_wallet(wallet) + , m_nodes(nodes) { ui->setupUi(this); @@ -47,16 +50,16 @@ void DebugInfoDialog::updateInfo() { ui->label_featherVersion->setText(QString("%1-%2").arg(FEATHER_VERSION, FEATHER_COMMIT)); - ui->label_walletHeight->setText(QString::number(m_ctx->wallet->blockChainHeight())); - ui->label_daemonHeight->setText(QString::number(m_ctx->wallet->daemonBlockChainHeight())); - ui->label_targetHeight->setText(QString::number(m_ctx->wallet->daemonBlockChainTargetHeight())); - QDateTime restoreDate = appData()->restoreHeights[constants::networkType]->heightToDate(m_ctx->wallet->getWalletCreationHeight()); - ui->label_restoreHeight->setText(QString("%1 (%2)").arg(QString::number(m_ctx->wallet->getWalletCreationHeight()), restoreDate.toString("yyyy-MM-dd"))); - ui->label_synchronized->setText(m_ctx->wallet->isSynchronized() ? "True" : "False"); + ui->label_walletHeight->setText(QString::number(m_wallet->blockChainHeight())); + ui->label_daemonHeight->setText(QString::number(m_wallet->daemonBlockChainHeight())); + ui->label_targetHeight->setText(QString::number(m_wallet->daemonBlockChainTargetHeight())); + QDateTime restoreDate = appData()->restoreHeights[constants::networkType]->heightToDate(m_wallet->getWalletCreationHeight()); + ui->label_restoreHeight->setText(QString("%1 (%2)").arg(QString::number(m_wallet->getWalletCreationHeight()), restoreDate.toString("yyyy-MM-dd"))); + ui->label_synchronized->setText(m_wallet->isSynchronized() ? "True" : "False"); - auto node = m_ctx->nodes->connection(); + auto node = m_nodes->connection(); ui->label_remoteNode->setText(node.toAddress()); - ui->label_walletStatus->setText(this->statusToString(m_ctx->wallet->connectionStatus())); + ui->label_walletStatus->setText(this->statusToString(m_wallet->connectionStatus())); QString websocketStatus = Utils::QtEnumToString(websocketNotifier()->websocketClient.webSocket.state()).remove("State"); if (config()->get(Config::disableWebsocket).toBool()) { websocketStatus = "Disabled"; @@ -66,16 +69,16 @@ void DebugInfoDialog::updateInfo() { ui->label_torLevel->setText(config()->get(Config::torPrivacyLevel).toString()); QString seedType = [this](){ - if (m_ctx->wallet->isHwBacked()) + if (m_wallet->isHwBacked()) return QString("Hardware"); - return QString("%1 word").arg(m_ctx->wallet->seedLength()); + return QString("%1 word").arg(m_wallet->seedLength()); }(); QString deviceType = [this](){ - if (m_ctx->wallet->isHwBacked()) { - if (m_ctx->wallet->isLedger()) + if (m_wallet->isHwBacked()) { + if (m_wallet->isLedger()) return "Ledger"; - else if (m_ctx->wallet->isTrezor()) + else if (m_wallet->isTrezor()) return "Trezor"; else return "Unknown"; @@ -85,15 +88,15 @@ void DebugInfoDialog::updateInfo() { } }(); - QString networkType = Utils::QtEnumToString(m_ctx->wallet->nettype()); + QString networkType = Utils::QtEnumToString(m_wallet->nettype()); if (config()->get(Config::offlineMode).toBool()) { networkType += " (offline)"; } ui->label_netType->setText(networkType); ui->label_seedType->setText(seedType); ui->label_deviceType->setText(deviceType); - ui->label_viewOnly->setText(m_ctx->wallet->viewOnly() ? "True" : "False"); - ui->label_primaryOnly->setText(m_ctx->wallet->balance(0) == m_ctx->wallet->balanceAll() ? "True" : "False"); + ui->label_viewOnly->setText(m_wallet->viewOnly() ? "True" : "False"); + ui->label_primaryOnly->setText(m_wallet->balance(0) == m_wallet->balanceAll() ? "True" : "False"); QString os = QSysInfo::prettyProductName(); if (TailsOS::detect()) { diff --git a/src/dialog/DebugInfoDialog.h b/src/dialog/DebugInfoDialog.h index a3224a4..53cb31f 100644 --- a/src/dialog/DebugInfoDialog.h +++ b/src/dialog/DebugInfoDialog.h @@ -6,9 +6,9 @@ #include -#include "appcontext.h" #include "components.h" #include "libwalletqt/Wallet.h" +#include "utils/nodes.h" namespace Ui { class DebugInfoDialog; @@ -19,7 +19,7 @@ class DebugInfoDialog : public WindowModalDialog Q_OBJECT public: - explicit DebugInfoDialog(QSharedPointer ctx, QWidget *parent = nullptr); + explicit DebugInfoDialog(Wallet *wallet, Nodes *nodes, QWidget *parent = nullptr); ~DebugInfoDialog() override; private: @@ -28,7 +28,8 @@ private: void updateInfo(); QScopedPointer ui; - QSharedPointer m_ctx; + Wallet *m_wallet; + Nodes *m_nodes; QTimer m_updateTimer; }; diff --git a/src/dialog/KeysDialog.cpp b/src/dialog/KeysDialog.cpp index 149dc76..1f459a3 100644 --- a/src/dialog/KeysDialog.cpp +++ b/src/dialog/KeysDialog.cpp @@ -4,7 +4,7 @@ #include "KeysDialog.h" #include "ui_KeysDialog.h" -KeysDialog::KeysDialog(QSharedPointer ctx, QWidget *parent) +KeysDialog::KeysDialog(Wallet *wallet, QWidget *parent) : WindowModalDialog(parent) , ui(new Ui::KeysDialog) { @@ -12,12 +12,12 @@ KeysDialog::KeysDialog(QSharedPointer ctx, QWidget *parent) QString unavailable = "Unavailable: Key is stored on hardware device"; - ui->label_restoreHeight->setText(QString::number(ctx->wallet->getWalletCreationHeight())); - ui->label_primaryAddress->setText(ctx->wallet->address(0, 0)); - ui->label_secretSpendKey->setText(ctx->wallet->isHwBacked() ? unavailable : ctx->wallet->getSecretSpendKey()); - ui->label_secretViewKey->setText(ctx->wallet->getSecretViewKey()); - ui->label_publicSpendKey->setText(ctx->wallet->getPublicSpendKey()); - ui->label_publicViewKey->setText(ctx->wallet->getPublicViewKey()); + ui->label_restoreHeight->setText(QString::number(wallet->getWalletCreationHeight())); + ui->label_primaryAddress->setText(wallet->address(0, 0)); + ui->label_secretSpendKey->setText(wallet->isHwBacked() ? unavailable : wallet->getSecretSpendKey()); + ui->label_secretViewKey->setText(wallet->getSecretViewKey()); + ui->label_publicSpendKey->setText(wallet->getPublicSpendKey()); + ui->label_publicViewKey->setText(wallet->getPublicViewKey()); this->adjustSize(); } diff --git a/src/dialog/KeysDialog.h b/src/dialog/KeysDialog.h index 1a92eaf..98b38be 100644 --- a/src/dialog/KeysDialog.h +++ b/src/dialog/KeysDialog.h @@ -6,8 +6,8 @@ #include -#include "appcontext.h" #include "components.h" +#include "libwalletqt/Wallet.h" namespace Ui { class KeysDialog; @@ -18,7 +18,7 @@ class KeysDialog : public WindowModalDialog Q_OBJECT public: - explicit KeysDialog(QSharedPointer ctx, QWidget *parent = nullptr); + explicit KeysDialog(Wallet *wallet, QWidget *parent = nullptr); ~KeysDialog() override; private: diff --git a/src/dialog/PaymentRequestDialog.cpp b/src/dialog/PaymentRequestDialog.cpp index 6457676..1899c38 100644 --- a/src/dialog/PaymentRequestDialog.cpp +++ b/src/dialog/PaymentRequestDialog.cpp @@ -10,11 +10,12 @@ #include #include "WalletManager.h" +#include "utils/Utils.h" -PaymentRequestDialog::PaymentRequestDialog(QWidget *parent, QSharedPointer ctx, QString address) +PaymentRequestDialog::PaymentRequestDialog(QWidget *parent, Wallet *wallet, QString address) : WindowModalDialog(parent) , ui(new Ui::PaymentRequestDialog) - , m_ctx(std::move(ctx)) + , m_wallet(wallet) , m_address(std::move(address)) { ui->setupUi(this); @@ -45,7 +46,7 @@ void PaymentRequestDialog::updatePaymentRequest() { QString recipient = ui->line_recipient->text(); quint64 amount = WalletManager::amountFromString(ui->line_amountXMR->text()); - QString uri = m_ctx->wallet->make_uri(m_address, amount, description, recipient); + QString uri = m_wallet->make_uri(m_address, amount, description, recipient); ui->line_paymentRequestUri->setText(uri); ui->line_paymentRequestUri->setCursorPosition(0); diff --git a/src/dialog/PaymentRequestDialog.h b/src/dialog/PaymentRequestDialog.h index f60c811..9a8eda4 100644 --- a/src/dialog/PaymentRequestDialog.h +++ b/src/dialog/PaymentRequestDialog.h @@ -6,8 +6,8 @@ #include -#include "appcontext.h" #include "components.h" +#include "libwalletqt/Wallet.h" #include "qrcode/QrCode.h" namespace Ui { @@ -19,7 +19,7 @@ class PaymentRequestDialog : public WindowModalDialog Q_OBJECT public: - explicit PaymentRequestDialog(QWidget *parent, QSharedPointer ctx, QString address); + explicit PaymentRequestDialog(QWidget *parent, Wallet *wallet, QString address); ~PaymentRequestDialog() override; private slots: @@ -30,7 +30,7 @@ private slots: private: QScopedPointer ui; - QSharedPointer m_ctx; + Wallet *m_wallet; QString m_address; QrCode *m_qrCode; }; diff --git a/src/dialog/SeedDialog.cpp b/src/dialog/SeedDialog.cpp index 60fc0ba..8fcd1ac 100644 --- a/src/dialog/SeedDialog.cpp +++ b/src/dialog/SeedDialog.cpp @@ -6,26 +6,26 @@ #include "constants.h" -SeedDialog::SeedDialog(QSharedPointer ctx, QWidget *parent) +SeedDialog::SeedDialog(Wallet *wallet, QWidget *parent) : WindowModalDialog(parent) , ui(new Ui::SeedDialog) - , m_ctx(std::move(ctx)) + , m_wallet(wallet) { ui->setupUi(this); ui->label_seedIcon->setPixmap(QPixmap(":/assets/images/seed.png").scaledToWidth(64, Qt::SmoothTransformation)); - ui->label_restoreHeight->setText(QString::number(m_ctx->wallet->getWalletCreationHeight())); + ui->label_restoreHeight->setText(QString::number(m_wallet->getWalletCreationHeight())); - if (m_ctx->wallet->getSeedLanguage().isEmpty()) { + if (m_wallet->getSeedLanguage().isEmpty()) { qDebug() << "No seed language set, using default"; - m_ctx->wallet->setSeedLanguage(constants::seedLanguage); + m_wallet->setSeedLanguage(constants::seedLanguage); } - QString seedOffset = m_ctx->wallet->getCacheAttribute("feather.seedoffset"); - QString seed = m_ctx->wallet->getCacheAttribute("feather.seed"); - auto seedLength = m_ctx->wallet->seedLength(); + QString seedOffset = m_wallet->getCacheAttribute("feather.seedoffset"); + QString seed = m_wallet->getCacheAttribute("feather.seed"); + auto seedLength = m_wallet->seedLength(); - QString seed_25_words = m_ctx->wallet->getSeed(seedOffset); + QString seed_25_words = m_wallet->getSeed(seedOffset); if (seedLength >= 24) { ui->check_toggleSeedType->hide(); diff --git a/src/dialog/SeedDialog.h b/src/dialog/SeedDialog.h index e2bdc2a..a293b17 100644 --- a/src/dialog/SeedDialog.h +++ b/src/dialog/SeedDialog.h @@ -6,8 +6,8 @@ #include -#include "appcontext.h" #include "components.h" +#include "libwalletqt/Wallet.h" namespace Ui { class SeedDialog; @@ -18,14 +18,14 @@ class SeedDialog : public WindowModalDialog Q_OBJECT public: - explicit SeedDialog(QSharedPointer ctx, QWidget *parent = nullptr); + explicit SeedDialog(Wallet *wallet, QWidget *parent = nullptr); ~SeedDialog() override; private: void setSeed(const QString &seed); QScopedPointer ui; - QSharedPointer m_ctx; + Wallet *m_wallet; }; diff --git a/src/dialog/TxBroadcastDialog.cpp b/src/dialog/TxBroadcastDialog.cpp index 9710449..50ede47 100644 --- a/src/dialog/TxBroadcastDialog.cpp +++ b/src/dialog/TxBroadcastDialog.cpp @@ -8,14 +8,14 @@ #include "utils/NetworkManager.h" -TxBroadcastDialog::TxBroadcastDialog(QWidget *parent, QSharedPointer ctx, const QString &transactionHex) +TxBroadcastDialog::TxBroadcastDialog(QWidget *parent, Nodes *nodes, const QString &transactionHex) : WindowModalDialog(parent) , ui(new Ui::TxBroadcastDialog) - , m_ctx(std::move(ctx)) + , m_nodes(nodes) { ui->setupUi(this); - auto node = m_ctx->nodes->connection(); + auto node = m_nodes->connection(); m_rpc = new DaemonRpc(this, node.toAddress()); connect(ui->btn_Broadcast, &QPushButton::clicked, this, &TxBroadcastDialog::broadcastTx); @@ -33,7 +33,7 @@ TxBroadcastDialog::TxBroadcastDialog(QWidget *parent, QSharedPointer void TxBroadcastDialog::broadcastTx() { QString tx = ui->transaction->toPlainText(); - FeatherNode node = ui->radio_useCustom->isChecked() ? FeatherNode(ui->customNode->text()) : m_ctx->nodes->connection(); + FeatherNode node = ui->radio_useCustom->isChecked() ? FeatherNode(ui->customNode->text()) : m_nodes->connection(); m_rpc->setDaemonAddress(node.toURL()); m_rpc->sendRawTransaction(tx); diff --git a/src/dialog/TxBroadcastDialog.h b/src/dialog/TxBroadcastDialog.h index 4d82632..f8ce212 100644 --- a/src/dialog/TxBroadcastDialog.h +++ b/src/dialog/TxBroadcastDialog.h @@ -6,9 +6,9 @@ #include -#include "appcontext.h" #include "components.h" #include "utils/daemonrpc.h" +#include "utils/nodes.h" namespace Ui { class TxBroadcastDialog; @@ -19,7 +19,7 @@ class TxBroadcastDialog : public WindowModalDialog Q_OBJECT public: - explicit TxBroadcastDialog(QWidget *parent, QSharedPointer ctx, const QString &transactionHex = ""); + explicit TxBroadcastDialog(QWidget *parent, Nodes *nodes, const QString &transactionHex = ""); ~TxBroadcastDialog() override; private slots: @@ -28,7 +28,7 @@ private slots: private: QScopedPointer ui; - QSharedPointer m_ctx; + Nodes *m_nodes; DaemonRpc *m_rpc; }; diff --git a/src/dialog/TxConfAdvDialog.cpp b/src/dialog/TxConfAdvDialog.cpp index 9ab1d69..0f2cafa 100644 --- a/src/dialog/TxConfAdvDialog.cpp +++ b/src/dialog/TxConfAdvDialog.cpp @@ -11,13 +11,17 @@ #include "dialog/QrCodeDialog.h" #include "libwalletqt/Input.h" #include "libwalletqt/Transfer.h" +#include "libwalletqt/WalletManager.h" #include "model/ModelUtils.h" #include "qrcode/QrCode.h" +#include "utils/AppData.h" +#include "utils/config.h" +#include "utils/Utils.h" -TxConfAdvDialog::TxConfAdvDialog(QSharedPointer ctx, const QString &description, QWidget *parent) +TxConfAdvDialog::TxConfAdvDialog(Wallet *wallet, const QString &description, QWidget *parent) : WindowModalDialog(parent) , ui(new Ui::TxConfAdvDialog) - , m_ctx(std::move(ctx)) + , m_wallet(wallet) , m_exportUnsignedMenu(new QMenu(this)) , m_exportSignedMenu(new QMenu(this)) , m_exportTxKeyMenu(new QMenu(this)) @@ -65,7 +69,7 @@ void TxConfAdvDialog::setTransaction(PendingTransaction *tx, bool isSigned) { PendingTransactionInfo *ptx = m_tx->transaction(0); //Todo: support split transactions // TODO: implement hasTxKey() - if (!m_ctx->wallet->isHwBacked() && m_tx->transaction(0)->txKey() == "0100000000000000000000000000000000000000000000000000000000000000") { + if (!m_wallet->isHwBacked() && m_tx->transaction(0)->txKey() == "0100000000000000000000000000000000000000000000000000000000000000") { ui->btn_exportTxKey->hide(); } @@ -148,7 +152,7 @@ void TxConfAdvDialog::setupConstructionData(ConstructionInfo *ci) { for (const auto& o: outputs) { auto address = o->address(); auto amount = WalletManager::displayAmount(o->amount()); - auto index = m_ctx->wallet->subaddressIndex(address); + auto index = m_wallet->subaddressIndex(address); cursor.insertText(address, Utils::addressTextFormat(index, o->amount())); cursor.insertText(QString(" %1").arg(amount), QTextCharFormat()); cursor.insertBlock(); @@ -205,7 +209,7 @@ void TxConfAdvDialog::signedCopy() { } void TxConfAdvDialog::txKeyCopy() { - if (m_ctx->wallet->isHwBacked()) { + if (m_wallet->isHwBacked()) { QMessageBox::warning(this, "Unable to get tx private key", "Unable to get tx secret key: wallet is backed by hardware device"); return; } @@ -218,15 +222,15 @@ void TxConfAdvDialog::signedQrCode() { void TxConfAdvDialog::broadcastTransaction() { if (m_tx == nullptr) return; - m_ctx->commitTransaction(m_tx, ui->line_description->text()); + m_wallet->commitTransaction(m_tx, ui->line_description->text()); QDialog::accept(); } void TxConfAdvDialog::closeDialog() { if (m_tx != nullptr) - m_ctx->wallet->disposeTransaction(m_tx); + m_wallet->disposeTransaction(m_tx); if (m_utx != nullptr) - m_ctx->wallet->disposeTransaction(m_utx); + m_wallet->disposeTransaction(m_utx); QDialog::reject(); } diff --git a/src/dialog/TxConfAdvDialog.h b/src/dialog/TxConfAdvDialog.h index 803fba9..443a455 100644 --- a/src/dialog/TxConfAdvDialog.h +++ b/src/dialog/TxConfAdvDialog.h @@ -9,9 +9,9 @@ #include #include -#include "appcontext.h" #include "components.h" #include "libwalletqt/PendingTransaction.h" +#include "libwalletqt/Wallet.h" namespace Ui { class TxConfAdvDialog; @@ -22,7 +22,7 @@ class TxConfAdvDialog : public WindowModalDialog Q_OBJECT public: - explicit TxConfAdvDialog(QSharedPointer ctx, const QString &description, QWidget *parent = nullptr); + explicit TxConfAdvDialog(Wallet *wallet, const QString &description, QWidget *parent = nullptr); ~TxConfAdvDialog() override; void setTransaction(PendingTransaction *tx, bool isSigned = true); // #TODO: have libwallet return a UnsignedTransaction, this is just dumb @@ -46,7 +46,7 @@ private: void txKeyCopy(); QScopedPointer ui; - QSharedPointer m_ctx; + Wallet *m_wallet; PendingTransaction *m_tx = nullptr; UnsignedTransaction *m_utx = nullptr; QMenu *m_exportUnsignedMenu; diff --git a/src/dialog/TxConfDialog.cpp b/src/dialog/TxConfDialog.cpp index 1b345d4..0254a95 100644 --- a/src/dialog/TxConfDialog.cpp +++ b/src/dialog/TxConfDialog.cpp @@ -11,11 +11,12 @@ #include "TxConfAdvDialog.h" #include "utils/AppData.h" #include "utils/ColorScheme.h" +#include "utils/config.h" -TxConfDialog::TxConfDialog(QSharedPointer ctx, PendingTransaction *tx, const QString &address, const QString &description, QWidget *parent) +TxConfDialog::TxConfDialog(Wallet *wallet, PendingTransaction *tx, const QString &address, const QString &description, QWidget *parent) : WindowModalDialog(parent) , ui(new Ui::TxConfDialog) - , m_ctx(std::move(ctx)) + , m_wallet(wallet) , m_tx(tx) , m_address(address) , m_description(description) @@ -53,7 +54,7 @@ TxConfDialog::TxConfDialog(QSharedPointer ctx, PendingTransaction *t ui->label_fee->setText(QString("%1 (%2 %3)").arg(amounts[1], amounts_fiat[1], preferredCur)); ui->label_total->setText(QString("%1 (%2 %3)").arg(amounts[2], amounts_fiat[2], preferredCur)); - auto subaddressIndex = m_ctx->wallet->subaddressIndex(address); + auto subaddressIndex = m_wallet->subaddressIndex(address); QString addressExtra; ui->label_address->setText(ModelUtils::displayAddress(address, 2)); diff --git a/src/dialog/TxConfDialog.h b/src/dialog/TxConfDialog.h index 7e148f9..d3a6286 100644 --- a/src/dialog/TxConfDialog.h +++ b/src/dialog/TxConfDialog.h @@ -6,10 +6,10 @@ #include -#include "appcontext.h" #include "components.h" #include "libwalletqt/PendingTransaction.h" #include "libwalletqt/WalletManager.h" +#include "libwalletqt/Wallet.h" namespace Ui { class TxConfDialog; @@ -20,7 +20,7 @@ class TxConfDialog : public WindowModalDialog Q_OBJECT public: - explicit TxConfDialog(QSharedPointer ctx, PendingTransaction *tx, const QString &address, const QString &description, QWidget *parent = nullptr); + explicit TxConfDialog(Wallet *wallet, PendingTransaction *tx, const QString &address, const QString &description, QWidget *parent = nullptr); ~TxConfDialog() override; bool showAdvanced = false; @@ -29,7 +29,7 @@ private: void setShowAdvanced(); QScopedPointer ui; - QSharedPointer m_ctx; + Wallet *m_wallet; PendingTransaction *m_tx; QString m_address; QString m_description; diff --git a/src/dialog/TxImportDialog.cpp b/src/dialog/TxImportDialog.cpp index f6a8217..c702ac5 100644 --- a/src/dialog/TxImportDialog.cpp +++ b/src/dialog/TxImportDialog.cpp @@ -8,10 +8,10 @@ #include "utils/NetworkManager.h" -TxImportDialog::TxImportDialog(QWidget *parent, QSharedPointer ctx) +TxImportDialog::TxImportDialog(QWidget *parent, Wallet *wallet) : WindowModalDialog(parent) , ui(new Ui::TxImportDialog) - , m_ctx(std::move(ctx)) + , m_wallet(wallet) { ui->setupUi(this); @@ -23,15 +23,15 @@ TxImportDialog::TxImportDialog(QWidget *parent, QSharedPointer ctx) void TxImportDialog::onImport() { QString txid = ui->line_txid->text(); - if (m_ctx->wallet->haveTransaction(txid)) { + if (m_wallet->haveTransaction(txid)) { QMessageBox::warning(this, "Warning", "This transaction already exists in the wallet. " "If you can't find it in your history, " "check if it belongs to a different account (Wallet -> Account)"); return; } - if (m_ctx->wallet->importTransaction(txid)) { - if (!m_ctx->wallet->haveTransaction(txid)) { + if (m_wallet->importTransaction(txid)) { + if (!m_wallet->haveTransaction(txid)) { QMessageBox::warning(this, "Import transaction", "This transaction does not belong to this wallet."); return; } @@ -39,7 +39,7 @@ void TxImportDialog::onImport() { } else { QMessageBox::warning(this, "Import transaction", "Transaction import failed."); } - m_ctx->refreshModels(); + m_wallet->refreshModels(); } TxImportDialog::~TxImportDialog() = default; diff --git a/src/dialog/TxImportDialog.h b/src/dialog/TxImportDialog.h index 6373328..2b9e452 100644 --- a/src/dialog/TxImportDialog.h +++ b/src/dialog/TxImportDialog.h @@ -6,9 +6,9 @@ #include -#include "appcontext.h" #include "components.h" #include "utils/daemonrpc.h" +#include "libwalletqt/Wallet.h" namespace Ui { class TxImportDialog; @@ -19,7 +19,7 @@ class TxImportDialog : public WindowModalDialog Q_OBJECT public: - explicit TxImportDialog(QWidget *parent, QSharedPointer ctx); + explicit TxImportDialog(QWidget *parent, Wallet *wallet); ~TxImportDialog() override; private slots: @@ -27,7 +27,7 @@ private slots: private: QScopedPointer ui; - QSharedPointer m_ctx; + Wallet *m_wallet; }; diff --git a/src/dialog/TxInfoDialog.cpp b/src/dialog/TxInfoDialog.cpp index 5a80ffd..4f98212 100644 --- a/src/dialog/TxInfoDialog.cpp +++ b/src/dialog/TxInfoDialog.cpp @@ -7,7 +7,6 @@ #include #include -#include "appcontext.h" #include "config.h" #include "constants.h" #include "libwalletqt/Coins.h" @@ -19,12 +18,12 @@ #include "Utils.h" #include "utils/Icons.h" -TxInfoDialog::TxInfoDialog(QSharedPointer ctx, TransactionInfo *txInfo, QWidget *parent) +TxInfoDialog::TxInfoDialog(Wallet *wallet, TransactionInfo *txInfo, QWidget *parent) : QDialog(parent) , ui(new Ui::TxInfoDialog) - , m_ctx(std::move(ctx)) + , m_wallet(wallet) , m_txInfo(txInfo) - , m_txProofDialog(new TxProofDialog(this, m_ctx, txInfo)) + , m_txProofDialog(new TxProofDialog(this, wallet, txInfo)) { ui->setupUi(this); @@ -39,7 +38,7 @@ TxInfoDialog::TxInfoDialog(QSharedPointer ctx, TransactionInfo *txIn connect(ui->btn_CopyTxKey, &QPushButton::clicked, this, &TxInfoDialog::copyTxKey); connect(ui->btn_createTxProof, &QPushButton::clicked, this, &TxInfoDialog::createTxProof); - connect(m_ctx->wallet, &Wallet::newBlock, this, &TxInfoDialog::updateData); + connect(m_wallet, &Wallet::newBlock, this, &TxInfoDialog::updateData); this->setData(txInfo); @@ -79,7 +78,7 @@ TxInfoDialog::TxInfoDialog(QSharedPointer ctx, TransactionInfo *txIn for (const auto& transfer : transfers) { auto address = transfer->address(); auto amount = WalletManager::displayAmount(transfer->amount()); - auto index = m_ctx->wallet->subaddressIndex(address); + auto index = m_wallet->subaddressIndex(address); cursor.insertText(address, Utils::addressTextFormat(index, transfer->amount())); cursor.insertText(QString(" %1").arg(amount), QTextCharFormat()); cursor.insertBlock(); @@ -151,7 +150,7 @@ void TxInfoDialog::setData(TransactionInfo *tx) { } void TxInfoDialog::updateData() { - TransactionInfo *tx = m_ctx->wallet->history()->transaction(m_txid); + TransactionInfo *tx = m_wallet->history()->transaction(m_txid); if (!tx) return; this->setData(tx); } @@ -161,12 +160,12 @@ void TxInfoDialog::copyTxID() { } void TxInfoDialog::copyTxKey() { - if (m_ctx->wallet->isHwBacked()) { + if (m_wallet->isHwBacked()) { QMessageBox::warning(this, "Unable to get tx private key", "Unable to get tx secret key: wallet is backed by hardware device"); return; } - m_ctx->wallet->getTxKeyAsync(m_txid, [this](QVariantMap map){ + m_wallet->getTxKeyAsync(m_txid, [this](QVariantMap map){ QString txKey = map.value("tx_key").toString(); if (txKey.isEmpty()) { QMessageBox::warning(this, "Unable to copy transaction key", "Transaction key unknown"); diff --git a/src/dialog/TxInfoDialog.h b/src/dialog/TxInfoDialog.h index 2f1cc79..b80e9e4 100644 --- a/src/dialog/TxInfoDialog.h +++ b/src/dialog/TxInfoDialog.h @@ -9,7 +9,6 @@ #include #include -#include "appcontext.h" #include "dialog/TxProofDialog.h" namespace Ui { @@ -21,7 +20,7 @@ class TxInfoDialog : public QDialog Q_OBJECT public: - explicit TxInfoDialog(QSharedPointer ctx, TransactionInfo *txInfo, QWidget *parent = nullptr); + explicit TxInfoDialog(Wallet *wallet, TransactionInfo *txInfo, QWidget *parent = nullptr); ~TxInfoDialog() override; signals: @@ -37,7 +36,7 @@ private: void viewOnBlockExplorer(); QScopedPointer ui; - QSharedPointer m_ctx; + Wallet *m_wallet; TransactionInfo *m_txInfo; TxProofDialog *m_txProofDialog; QString m_txid; diff --git a/src/dialog/TxProofDialog.cpp b/src/dialog/TxProofDialog.cpp index bff827e..b365787 100644 --- a/src/dialog/TxProofDialog.cpp +++ b/src/dialog/TxProofDialog.cpp @@ -10,10 +10,10 @@ #include "utils/Icons.h" #include "utils/Utils.h" -TxProofDialog::TxProofDialog(QWidget *parent, QSharedPointer ctx, TransactionInfo *txInfo) +TxProofDialog::TxProofDialog(QWidget *parent, Wallet *wallet, TransactionInfo *txInfo) : WindowModalDialog(parent) , ui(new Ui::TxProofDialog) - , m_ctx(std::move(ctx)) + , m_wallet(wallet) { ui->setupUi(this); @@ -26,7 +26,7 @@ TxProofDialog::TxProofDialog(QWidget *parent, QSharedPointer ctx, Tr } for (auto const &s: txInfo->subaddrIndex()) { - m_InDestinations.push_back(m_ctx->wallet->address(txInfo->subaddrAccount(), s)); + m_InDestinations.push_back(m_wallet->address(txInfo->subaddrAccount(), s)); } // Due to some logic in core we can't create OutProofs @@ -57,7 +57,7 @@ TxProofDialog::TxProofDialog(QWidget *parent, QSharedPointer ctx, Tr void TxProofDialog::getTxKey() { if (!m_txKey.isEmpty()) return; - m_ctx->wallet->getTxKeyAsync(m_txid, [this](QVariantMap map){ + m_wallet->getTxKeyAsync(m_txid, [this](QVariantMap map){ m_txKey = map.value("tx_key").toString(); }); } @@ -75,7 +75,7 @@ void TxProofDialog::selectSpendProof() { return; } - if (m_ctx->wallet->isHwBacked()) { + if (m_wallet->isHwBacked()) { this->showWarning("SpendProof creation is not supported on this hardware device."); return; } @@ -150,7 +150,7 @@ void TxProofDialog::showWarning(const QString &message) { void TxProofDialog::getFormattedProof() { QString message = ui->message->toPlainText(); QString address = ui->combo_address->currentText(); - QString nettype = Utils::QtEnumToString(m_ctx->wallet->nettype()).toLower(); + QString nettype = Utils::QtEnumToString(m_wallet->nettype()).toLower(); nettype = nettype.replace(0, 1, nettype[0].toUpper()); // Capitalize first letter TxProof proof = this->getProof(); @@ -231,12 +231,12 @@ TxProof TxProofDialog::getProof() { TxProof proof = [this, message, address]{ switch (m_mode) { case Mode::SpendProof: { - return m_ctx->wallet->getSpendProof(m_txid, message); + return m_wallet->getSpendProof(m_txid, message); } case Mode::OutProof: case Mode::InProof: default: { // Todo: split this into separate functions - return m_ctx->wallet->getTxProof(m_txid, address, message); + return m_wallet->getTxProof(m_txid, address, message); } } }(); diff --git a/src/dialog/TxProofDialog.h b/src/dialog/TxProofDialog.h index cb69719..8f14d97 100644 --- a/src/dialog/TxProofDialog.h +++ b/src/dialog/TxProofDialog.h @@ -6,9 +6,9 @@ #include -#include "appcontext.h" #include "components.h" #include "libwalletqt/TransactionInfo.h" +#include "libwalletqt/Wallet.h" namespace Ui { class TxProofDialog; @@ -19,7 +19,7 @@ class TxProofDialog : public WindowModalDialog Q_OBJECT public: - explicit TxProofDialog(QWidget *parent, QSharedPointer ctx, TransactionInfo *txid); + explicit TxProofDialog(QWidget *parent, Wallet *wallet, TransactionInfo *txid); ~TxProofDialog() override; void setTxId(const QString &txid); void getTxKey(); @@ -45,7 +45,7 @@ private: void showWarning(const QString &message); QScopedPointer ui; - QSharedPointer m_ctx; + Wallet *m_wallet; QStringList m_OutDestinations; QStringList m_InDestinations; diff --git a/src/dialog/ViewOnlyDialog.cpp b/src/dialog/ViewOnlyDialog.cpp index 65e428e..1ea4549 100644 --- a/src/dialog/ViewOnlyDialog.cpp +++ b/src/dialog/ViewOnlyDialog.cpp @@ -8,21 +8,23 @@ #include #include -ViewOnlyDialog::ViewOnlyDialog(QSharedPointer ctx, QWidget *parent) +#include "utils/Utils.h" + +ViewOnlyDialog::ViewOnlyDialog(Wallet *wallet, QWidget *parent) : WindowModalDialog(parent) , ui(new Ui::ViewOnlyDialog) - , m_ctx(std::move(ctx)) + , m_wallet(wallet) { ui->setupUi(this); - ui->label_restoreHeight->setText(QString::number(m_ctx->wallet->getWalletCreationHeight())); - ui->label_primaryAddress->setText(m_ctx->wallet->address(0, 0)); - ui->label_secretViewKey->setText(m_ctx->wallet->getSecretViewKey()); + ui->label_restoreHeight->setText(QString::number(m_wallet->getWalletCreationHeight())); + ui->label_primaryAddress->setText(m_wallet->address(0, 0)); + ui->label_secretViewKey->setText(m_wallet->getSecretViewKey()); connect(ui->btn_Copy, &QPushButton::clicked, this, &ViewOnlyDialog::copyToClipboard); connect(ui->btn_Save, &QPushButton::clicked, this, &ViewOnlyDialog::onWriteViewOnlyWallet); - if (m_ctx->wallet->viewOnly()) { + if (m_wallet->viewOnly()) { ui->btn_Save->setEnabled(false); ui->btn_Save->setToolTip("Wallet is already view-only"); } @@ -45,7 +47,7 @@ void ViewOnlyDialog::onWriteViewOnlyWallet(){ if((bool)passwordDialog.exec()) passwd = passwordDialog.textValue(); - m_ctx->wallet->createViewOnly(fn, passwd); + m_wallet->createViewOnly(fn, passwd); QMessageBox::information(this, "Information", "View-only wallet successfully written to disk."); } diff --git a/src/dialog/ViewOnlyDialog.h b/src/dialog/ViewOnlyDialog.h index 0954ade..08de238 100644 --- a/src/dialog/ViewOnlyDialog.h +++ b/src/dialog/ViewOnlyDialog.h @@ -6,8 +6,8 @@ #include -#include "appcontext.h" #include "components.h" +#include "libwalletqt/Wallet.h" namespace Ui { class ViewOnlyDialog; @@ -18,7 +18,7 @@ class ViewOnlyDialog : public WindowModalDialog Q_OBJECT public: - explicit ViewOnlyDialog(QSharedPointer ctx, QWidget *parent = nullptr); + explicit ViewOnlyDialog(Wallet *wallet, QWidget *parent = nullptr); ~ViewOnlyDialog() override; private slots: @@ -28,7 +28,7 @@ private: void copyToClipboard(); QScopedPointer ui; - QSharedPointer m_ctx; + Wallet *m_wallet; }; diff --git a/src/dialog/WalletCacheDebugDialog.cpp b/src/dialog/WalletCacheDebugDialog.cpp index d245ea0..97ed0d2 100644 --- a/src/dialog/WalletCacheDebugDialog.cpp +++ b/src/dialog/WalletCacheDebugDialog.cpp @@ -8,81 +8,81 @@ #include "model/ModelUtils.h" -WalletCacheDebugDialog::WalletCacheDebugDialog(QSharedPointer ctx, QWidget *parent) +WalletCacheDebugDialog::WalletCacheDebugDialog(Wallet *wallet, QWidget *parent) : WindowModalDialog(parent) , ui(new Ui::WalletCacheDebugDialog) - , m_ctx(std::move(ctx)) + , m_wallet(wallet) { ui->setupUi(this); ui->output->setFont(ModelUtils::getMonospaceFont()); connect(ui->m_blockchain, &QRadioButton::pressed, [this]{ - this->setOutput(m_ctx->wallet->printBlockchain()); + this->setOutput(m_wallet->printBlockchain()); }); connect(ui->m_transfers, &QRadioButton::pressed, [this]{ - this->setOutput(m_ctx->wallet->printTransfers()); + this->setOutput(m_wallet->printTransfers()); }); connect(ui->m_unconfirmed_payments, &QRadioButton::pressed, [this]{ - this->setOutput(m_ctx->wallet->printUnconfirmedPayments()); + this->setOutput(m_wallet->printUnconfirmedPayments()); }); connect(ui->m_confirmed_txs, &QRadioButton::pressed, [this]{ - this->setOutput(m_ctx->wallet->printConfirmedTransferDetails()); + this->setOutput(m_wallet->printConfirmedTransferDetails()); }); connect(ui->m_unconfirmed_txs, &QRadioButton::pressed, [this]{ - this->setOutput(m_ctx->wallet->printUnconfirmedTransferDetails()); + this->setOutput(m_wallet->printUnconfirmedTransferDetails()); }); connect(ui->m_payments, &QRadioButton::pressed, [this]{ - this->setOutput(m_ctx->wallet->printPayments()); + this->setOutput(m_wallet->printPayments()); }); connect(ui->m_pub_keys, &QRadioButton::pressed, [this]{ - this->setOutput(m_ctx->wallet->printPubKeys()); + this->setOutput(m_wallet->printPubKeys()); }); connect(ui->m_tx_notes, &QRadioButton::pressed, [this]{ - this->setOutput(m_ctx->wallet->printTxNotes()); + this->setOutput(m_wallet->printTxNotes()); }); connect(ui->m_subaddresses, &QRadioButton::pressed, [this]{ - this->setOutput(m_ctx->wallet->printSubaddresses()); + this->setOutput(m_wallet->printSubaddresses()); }); connect(ui->m_subaddress_labels, &QRadioButton::pressed, [this]{ - this->setOutput(m_ctx->wallet->printSubaddressLabels()); + this->setOutput(m_wallet->printSubaddressLabels()); }); connect(ui->m_additional_tx_keys, &QRadioButton::pressed, [this]{ - this->setOutput(m_ctx->wallet->printAdditionalTxKeys()); + this->setOutput(m_wallet->printAdditionalTxKeys()); }); connect(ui->m_attributes, &QRadioButton::pressed, [this]{ - this->setOutput(m_ctx->wallet->printAttributes()); + this->setOutput(m_wallet->printAttributes()); }); connect(ui->m_key_images, &QRadioButton::pressed, [this]{ - this->setOutput(m_ctx->wallet->printKeyImages()); + this->setOutput(m_wallet->printKeyImages()); }); connect(ui->m_account_tags, &QRadioButton::pressed, [this]{ - this->setOutput(m_ctx->wallet->printAccountTags()); + this->setOutput(m_wallet->printAccountTags()); }); connect(ui->m_tx_keys, &QRadioButton::pressed, [this]{ - this->setOutput(m_ctx->wallet->printTxKeys()); + this->setOutput(m_wallet->printTxKeys()); }); connect(ui->m_address_book, &QRadioButton::pressed, [this]{ - this->setOutput(m_ctx->wallet->printAddressBook()); + this->setOutput(m_wallet->printAddressBook()); }); connect(ui->m_scanned_pool_txs, &QRadioButton::pressed, [this]{ - this->setOutput(m_ctx->wallet->printScannedPoolTxs()); + this->setOutput(m_wallet->printScannedPoolTxs()); }); this->adjustSize(); diff --git a/src/dialog/WalletCacheDebugDialog.h b/src/dialog/WalletCacheDebugDialog.h index bee1510..13c6ed5 100644 --- a/src/dialog/WalletCacheDebugDialog.h +++ b/src/dialog/WalletCacheDebugDialog.h @@ -6,8 +6,8 @@ #include -#include "appcontext.h" #include "components.h" +#include "libwalletqt/Wallet.h" namespace Ui { class WalletCacheDebugDialog; @@ -18,14 +18,14 @@ class WalletCacheDebugDialog : public WindowModalDialog Q_OBJECT public: - explicit WalletCacheDebugDialog(QSharedPointer ctx, QWidget *parent = nullptr); + explicit WalletCacheDebugDialog(Wallet *wallet, QWidget *parent = nullptr); ~WalletCacheDebugDialog() override; private: void setOutput(const QString &output); QScopedPointer ui; - QSharedPointer m_ctx; + Wallet *m_wallet; }; diff --git a/src/dialog/WalletInfoDialog.cpp b/src/dialog/WalletInfoDialog.cpp index b93df83..d3a1d80 100644 --- a/src/dialog/WalletInfoDialog.cpp +++ b/src/dialog/WalletInfoDialog.cpp @@ -6,22 +6,24 @@ #include -WalletInfoDialog::WalletInfoDialog(QSharedPointer ctx, QWidget *parent) +#include "utils/Utils.h" + +WalletInfoDialog::WalletInfoDialog(Wallet *wallet, QWidget *parent) : WindowModalDialog(parent) , ui(new Ui::WalletInfoDialog) - , m_ctx(std::move(ctx)) + , m_wallet(wallet) { ui->setupUi(this); - QFileInfo cache(m_ctx->wallet->cachePath()); + QFileInfo cache(m_wallet->cachePath()); - ui->label_walletName->setText(QFileInfo(m_ctx->wallet->cachePath()).fileName()); - ui->label_netType->setText(Utils::QtEnumToString(m_ctx->wallet->nettype())); - ui->label_seedType->setText(QString("%1 word").arg(m_ctx->wallet->seedLength())); - ui->label_viewOnly->setText(m_ctx->wallet->viewOnly() ? "True" : "False"); - ui->label_subaddressLookahead->setText(m_ctx->wallet->getSubaddressLookahead()); - ui->label_keysFile->setText(m_ctx->wallet->keysPath()); - ui->label_cacheFile->setText(m_ctx->wallet->cachePath()); + ui->label_walletName->setText(QFileInfo(m_wallet->cachePath()).fileName()); + ui->label_netType->setText(Utils::QtEnumToString(m_wallet->nettype())); + ui->label_seedType->setText(QString("%1 word").arg(m_wallet->seedLength())); + ui->label_viewOnly->setText(m_wallet->viewOnly() ? "True" : "False"); + ui->label_subaddressLookahead->setText(m_wallet->getSubaddressLookahead()); + ui->label_keysFile->setText(m_wallet->keysPath()); + ui->label_cacheFile->setText(m_wallet->cachePath()); ui->label_cacheSize->setText(QString("%1 MB").arg(QString::number(cache.size() / 1e6, 'f', 2))); connect(ui->btn_openWalletDir, &QPushButton::clicked, this, &WalletInfoDialog::openWalletDir); @@ -40,7 +42,7 @@ WalletInfoDialog::WalletInfoDialog(QSharedPointer ctx, QWidget *pare } void WalletInfoDialog::openWalletDir() { - QFileInfo file(m_ctx->wallet->keysPath()); + QFileInfo file(m_wallet->keysPath()); QDesktopServices::openUrl(QUrl::fromLocalFile(file.absolutePath())); } diff --git a/src/dialog/WalletInfoDialog.h b/src/dialog/WalletInfoDialog.h index b25af70..52bac95 100644 --- a/src/dialog/WalletInfoDialog.h +++ b/src/dialog/WalletInfoDialog.h @@ -6,8 +6,8 @@ #include -#include "appcontext.h" #include "components.h" +#include "libwalletqt/Wallet.h" namespace Ui { class WalletInfoDialog; @@ -18,14 +18,14 @@ class WalletInfoDialog : public WindowModalDialog Q_OBJECT public: - explicit WalletInfoDialog(QSharedPointer ctx, QWidget *parent = nullptr); + explicit WalletInfoDialog(Wallet *wallet, QWidget *parent = nullptr); ~WalletInfoDialog() override; private: void openWalletDir(); QScopedPointer ui; - QSharedPointer m_ctx; + Wallet *m_wallet; }; #endif //FEATHER_WALLETINFODIALOG_H diff --git a/src/libwalletqt/CMakeLists.txt b/src/libwalletqt/CMakeLists.txt deleted file mode 100644 index e69de29..0000000 diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp index 6cbbc89..2ed4e36 100644 --- a/src/libwalletqt/Wallet.cpp +++ b/src/libwalletqt/Wallet.cpp @@ -6,11 +6,16 @@ #include #include -#include "TransactionHistory.h" #include "AddressBook.h" +#include "Coins.h" #include "Subaddress.h" #include "SubaddressAccount.h" -#include "Coins.h" +#include "TransactionHistory.h" +#include "WalletManager.h" + +#include "config.h" +#include "constants.h" + #include "model/TransactionHistoryModel.h" #include "model/TransactionHistoryProxyModel.h" #include "model/AddressBookModel.h" @@ -21,26 +26,219 @@ #include "utils/ScopeGuard.h" namespace { - static constexpr char ATTRIBUTE_SUBADDRESS_ACCOUNT[] = "feather.subaddress_account"; + constexpr char ATTRIBUTE_SUBADDRESS_ACCOUNT[] = "feather.subaddress_account"; } -Wallet::Wallet(QObject * parent) - : Wallet(nullptr, parent) +Wallet::Wallet(Monero::Wallet *wallet, QObject *parent) + : QObject(parent) + , m_walletImpl(wallet) + , m_history(new TransactionHistory(m_walletImpl->history(), this)) + , m_historyModel(nullptr) + , m_addressBook(new AddressBook(m_walletImpl->addressBook(), this)) + , m_addressBookModel(nullptr) + , m_daemonBlockChainHeight(0) + , m_daemonBlockChainTargetHeight(0) + , m_connectionStatus(Wallet::ConnectionStatus_Disconnected) + , m_currentSubaddressAccount(0) + , m_subaddress(new Subaddress(m_walletImpl->subaddress(), this)) + , m_subaddressAccount(new SubaddressAccount(m_walletImpl->subaddressAccount(), this)) + , m_refreshNow(false) + , m_refreshEnabled(false) + , m_scheduler(this) + , m_useSSL(true) + , m_coins(new Coins(m_walletImpl->coins(), this)) + , m_storeTimer(new QTimer(this)) { + m_walletListener = new WalletListenerImpl(this); + m_walletImpl->setListener(m_walletListener); + m_currentSubaddressAccount = getCacheAttribute(ATTRIBUTE_SUBADDRESS_ACCOUNT).toUInt(); + + m_addressBookModel = new AddressBookModel(this, m_addressBook); + m_subaddressModel = new SubaddressModel(this, m_subaddress); + m_subaddressAccountModel = new SubaddressAccountModel(this, m_subaddressAccount); + m_coinsModel = new CoinsModel(this, m_coins); + + if (this->status() == Status_Ok) { + startRefreshThread(); + + // Store the wallet every 2 minutes + m_storeTimer->start(2 * 60 * 1000); + connect(m_storeTimer, &QTimer::timeout, [this](){ + this->storeSafer(); + }); + + this->updateBalance(); + } + + connect(this->history(), &TransactionHistory::txNoteChanged, [this]{ + this->history()->refresh(this->currentSubaddressAccount()); + }); + + connect(this, &Wallet::createTransactionError, this, &Wallet::onCreateTransactionError); + connect(this, &Wallet::refreshed, this, &Wallet::onRefreshed); + connect(this, &Wallet::newBlock, this, &Wallet::onNewBlock); + connect(this, &Wallet::updated, this, &Wallet::onUpdated); + connect(this, &Wallet::heightsRefreshed, this, &Wallet::onHeightsRefreshed); + connect(this, &Wallet::transactionCreated, this, &Wallet::onTransactionCreated); } -Wallet::ConnectionStatus Wallet::connectionStatus() const -{ +// #################### Status #################### + +Wallet::Status Wallet::status() const { + return static_cast(m_walletImpl->status()); +} + +Wallet::ConnectionStatus Wallet::connectionStatus() const { return m_connectionStatus; } -QString Wallet::getSeed(const QString &seedOffset) const -{ +void Wallet::setConnectionStatus(ConnectionStatus value) { + if (m_connectionStatus == value) { + return; + } + + m_connectionStatus = value; + emit connectionStatusChanged(m_connectionStatus); +} + +bool Wallet::isSynchronized() const { + return connectionStatus() == ConnectionStatus_Synchronized; +} + +bool Wallet::isConnected() const { + auto status = connectionStatus(); + return status == ConnectionStatus_Synchronizing || status == ConnectionStatus_Synchronized; +} + +QString Wallet::errorString() const { + return QString::fromStdString(m_walletImpl->errorString()); +} + +NetworkType::Type Wallet::nettype() const { + return static_cast(m_walletImpl->nettype()); +} + +bool Wallet::viewOnly() const { + return m_walletImpl->watchOnly(); +} + +bool Wallet::isDeterministic() const { + return m_walletImpl->isDeterministic(); +} + +// #################### Balance #################### + +quint64 Wallet::balance() const { + return balance(m_currentSubaddressAccount); +} + +quint64 Wallet::balance(quint32 accountIndex) const { + return m_walletImpl->balance(accountIndex); +} + +quint64 Wallet::balanceAll() const { + return m_walletImpl->balanceAll(); +} + +quint64 Wallet::unlockedBalance() const { + return unlockedBalance(m_currentSubaddressAccount); +} + +quint64 Wallet::unlockedBalance(quint32 accountIndex) const { + return m_walletImpl->unlockedBalance(accountIndex); +} + +quint64 Wallet::unlockedBalanceAll() const { + return m_walletImpl->unlockedBalanceAll(); +} + +void Wallet::updateBalance() { + quint64 balance = this->balance(); + quint64 spendable = this->unlockedBalance(); + + emit balanceUpdated(balance, spendable); +} + +// #################### Subaddresses and Accounts #################### + +QString Wallet::address(quint32 accountIndex, quint32 addressIndex) const { + return QString::fromStdString(m_walletImpl->address(accountIndex, addressIndex)); +} + +SubaddressIndex Wallet::subaddressIndex(const QString &address) const { + std::pair i; + if (!m_walletImpl->subaddressIndex(address.toStdString(), i)) { + return SubaddressIndex(-1, -1); + } + return SubaddressIndex(i.first, i.second); +} + +quint32 Wallet::currentSubaddressAccount() const { + return m_currentSubaddressAccount; +} + +void Wallet::switchSubaddressAccount(quint32 accountIndex) { + if (accountIndex < numSubaddressAccounts()) + { + m_currentSubaddressAccount = accountIndex; + if (!setCacheAttribute(ATTRIBUTE_SUBADDRESS_ACCOUNT, QString::number(m_currentSubaddressAccount))) + { + qWarning() << "failed to set " << ATTRIBUTE_SUBADDRESS_ACCOUNT << " cache attribute"; + } + m_subaddress->refresh(m_currentSubaddressAccount); + m_history->refresh(m_currentSubaddressAccount); + m_coins->refresh(m_currentSubaddressAccount); + this->subaddressModel()->setCurrentSubaddressAccount(m_currentSubaddressAccount); + this->coinsModel()->setCurrentSubaddressAccount(m_currentSubaddressAccount); + this->updateBalance(); + emit currentSubaddressAccountChanged(); + } +} +void Wallet::addSubaddressAccount(const QString& label) { + m_walletImpl->addSubaddressAccount(label.toStdString()); + switchSubaddressAccount(numSubaddressAccounts() - 1); +} + +quint32 Wallet::numSubaddressAccounts() const { + return m_walletImpl->numSubaddressAccounts(); +} + +quint32 Wallet::numSubaddresses(quint32 accountIndex) const { + return m_walletImpl->numSubaddresses(accountIndex); +} + +void Wallet::addSubaddress(const QString& label) { + m_walletImpl->addSubaddress(currentSubaddressAccount(), label.toStdString()); +} + +QString Wallet::getSubaddressLabel(quint32 accountIndex, quint32 addressIndex) const { + return QString::fromStdString(m_walletImpl->getSubaddressLabel(accountIndex, addressIndex)); +} + +void Wallet::setSubaddressLabel(quint32 accountIndex, quint32 addressIndex, const QString &label) { + m_walletImpl->setSubaddressLabel(accountIndex, addressIndex, label.toStdString()); +} + +void Wallet::deviceShowAddressAsync(quint32 accountIndex, quint32 addressIndex, const QString &paymentId) { + m_scheduler.run([this, accountIndex, addressIndex, paymentId] { + m_walletImpl->deviceShowAddress(accountIndex, addressIndex, paymentId.toStdString()); + emit deviceShowAddressShowed(); + }); +} + +QString Wallet::getSubaddressLookahead() const { + auto lookahead = m_walletImpl->getSubaddressLookahead(); + + return QString("%1:%2").arg(QString::number(lookahead.first), QString::number(lookahead.second)); +} + +// #################### Seed #################### + +QString Wallet::getSeed(const QString &seedOffset) const { return QString::fromStdString(m_walletImpl->seed(seedOffset.toStdString())); } -qsizetype Wallet::seedLength() const -{ +qsizetype Wallet::seedLength() const { auto seedLength = this->getCacheAttribute("feather.seed").split(" ", Qt::SkipEmptyParts).length(); return seedLength ? seedLength : 25; } @@ -55,51 +253,165 @@ void Wallet::setSeedLanguage(const QString &lang) m_walletImpl->setSeedLanguage(lang.toStdString()); } -Wallet::Status Wallet::status() const -{ - return static_cast(m_walletImpl->status()); +// #################### Node connection #################### + +void Wallet::setOffline(bool offline) const { + return m_walletImpl->setOffline(offline); } -NetworkType::Type Wallet::nettype() const -{ - return static_cast(m_walletImpl->nettype()); +void Wallet::setTrustedDaemon(bool arg) { + m_walletImpl->setTrustedDaemon(arg); } -bool Wallet::disconnected() const -{ - return m_disconnected; +void Wallet::setUseSSL(bool ssl) { + m_useSSL = ssl; } -bool Wallet::refreshing() const -{ - return m_refreshing; +void Wallet::setDaemonLogin(const QString &daemonUsername, const QString &daemonPassword) { + m_daemonUsername = daemonUsername; + m_daemonPassword = daemonPassword; } -void Wallet::refreshingSet(bool value) +void Wallet::initAsync(const QString &daemonAddress, bool trustedDaemon, quint64 upperTransactionLimit, const QString &proxyAddress) { - if (m_refreshing.exchange(value) != value) + qDebug() << "initAsync: " + daemonAddress; + const auto future = m_scheduler.run([this, daemonAddress, trustedDaemon, upperTransactionLimit, proxyAddress] { + // Beware! This code does not run in the GUI thread. + + bool success; + { + QMutexLocker locker(&m_proxyMutex); + success = m_walletImpl->init(daemonAddress.toStdString(), upperTransactionLimit, m_daemonUsername.toStdString(), m_daemonPassword.toStdString(), m_useSSL, false, proxyAddress.toStdString()); + } + + setTrustedDaemon(trustedDaemon); + + if (success) { + qDebug() << "init async finished - starting refresh"; + startRefresh(); + } + }); + if (future.first) { - emit refreshingChanged(); + setConnectionStatus(Wallet::ConnectionStatus_Connecting); } } -void Wallet::setConnectionStatus(ConnectionStatus value) +// #################### Synchronization (Refresh) #################### + +void Wallet::startRefresh() { + m_refreshEnabled = true; + m_refreshNow = true; +} + +void Wallet::pauseRefresh() { + m_refreshEnabled = false; +} + +void Wallet::startRefreshThread() { - if (m_connectionStatus == value) + const auto future = m_scheduler.run([this] { + // Beware! This code does not run in the GUI thread. + + constexpr const std::chrono::seconds refreshInterval{10}; + constexpr const std::chrono::milliseconds intervalResolution{100}; + + auto last = std::chrono::steady_clock::now(); + while (!m_scheduler.stopping()) + { + if (m_refreshEnabled && (!isHwBacked() || isDeviceConnected())) + { + const auto now = std::chrono::steady_clock::now(); + const auto elapsed = now - last; + if (elapsed >= refreshInterval || m_refreshNow) + { + m_refreshNow = false; + + // get daemonHeight and targetHeight + // daemonHeight and targetHeight will be 0 if call to get_info fails + quint64 daemonHeight = m_walletImpl->daemonBlockChainHeight(); + bool success = daemonHeight > 0; + + quint64 targetHeight = 0; + if (success) { + targetHeight = m_walletImpl->daemonBlockChainTargetHeight(); + } + bool haveHeights = (daemonHeight > 0 && targetHeight > 0); + + emit heightsRefreshed(haveHeights, daemonHeight, targetHeight); + + // Don't call refresh function if we don't have the daemon and target height + // We do this to prevent to UI from getting confused about the amount of blocks that are still remaining + if (haveHeights) { + QMutexLocker locker(&m_asyncMutex); + + if (m_newWallet) { + // Set blockheight to daemonHeight for newly created wallets to speed up initial sync + m_walletImpl->setRefreshFromBlockHeight(daemonHeight); + m_newWallet = false; + } + + m_walletImpl->refresh(); + } + last = std::chrono::steady_clock::now(); + } + } + + std::this_thread::sleep_for(intervalResolution); + } + }); + if (!future.first) { - return; + throw std::runtime_error("failed to start auto refresh thread"); } +} - m_connectionStatus = value; - emit connectionStatusChanged(m_connectionStatus); +void Wallet::onHeightsRefreshed(bool success, quint64 daemonHeight, quint64 targetHeight) { + m_daemonBlockChainHeight = daemonHeight; + m_daemonBlockChainTargetHeight = targetHeight; - bool disconnected = m_connectionStatus == Wallet::ConnectionStatus_Connecting || - m_connectionStatus == Wallet::ConnectionStatus_Disconnected; + if (success) { + quint64 walletHeight = blockChainHeight(); - if (m_disconnected != disconnected) - { - m_disconnected = disconnected; - emit disconnectedChanged(); + if (this->connectionStatus() != Wallet::ConnectionStatus_Disconnected) { + if (daemonHeight < targetHeight) { + emit blockchainSync(daemonHeight, targetHeight); + } + else { + this->syncStatusUpdated(walletHeight, daemonHeight); + } + } + + if (walletHeight < (daemonHeight - 1)) { + setConnectionStatus(ConnectionStatus_Synchronizing); + } else { + setConnectionStatus(ConnectionStatus_Synchronized); + } + } else { + setConnectionStatus(ConnectionStatus_Disconnected); + } +} + +quint64 Wallet::blockChainHeight() const { + // Can not block UI + return m_walletImpl->blockChainHeight(); +} + +quint64 Wallet::daemonBlockChainHeight() const { + return m_daemonBlockChainHeight; +} + +quint64 Wallet::daemonBlockChainTargetHeight() const { + return m_daemonBlockChainTargetHeight; +} + +void Wallet::syncStatusUpdated(quint64 height, quint64 target) { + if (height < (target - 1)) { + emit refreshSync(height, target); + } + else { + this->updateBalance(); + emit synchronized(); } } @@ -112,385 +424,96 @@ void Wallet::onNewBlock(uint64_t walletHeight) { } else { setConnectionStatus(ConnectionStatus_Synchronized); } -} -QString Wallet::getProxyAddress() const -{ - QMutexLocker locker(&m_proxyMutex); - return m_proxyAddress; -} + this->syncStatusUpdated(walletHeight, daemonHeight); -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 -{ - // Misleading: this will return true if wallet has synchronized at least once even if it isn't currently synchronized - return m_walletImpl->synchronized(); -} - -bool Wallet::isSynchronized() const -{ - return connectionStatus() == ConnectionStatus_Synchronized; -} - -bool Wallet::isConnected() const -{ - auto status = connectionStatus(); - return status == ConnectionStatus_Synchronizing || status == ConnectionStatus_Synchronized; -} - -void Wallet::setOffline(bool offline) const -{ - return m_walletImpl->setOffline(offline); -} - -QString Wallet::errorString() const -{ - return QString::fromStdString(m_walletImpl->errorString()); -} - -bool Wallet::setPassword(const QString &oldPassword, const QString &newPassword) -{ - return m_walletImpl->setPassword(oldPassword.toStdString(), newPassword.toStdString()); -} - -bool Wallet::verifyPassword(const QString &password) -{ - return m_walletImpl->verifyPassword(password.toStdString()); -} - -QString Wallet::address(quint32 accountIndex, quint32 addressIndex) const -{ - return QString::fromStdString(m_walletImpl->address(accountIndex, addressIndex)); -} - -SubaddressIndex Wallet::subaddressIndex(const QString &address) const -{ - std::pair i; - if (!m_walletImpl->subaddressIndex(address.toStdString(), i)) { - return SubaddressIndex(-1, -1); - } - return SubaddressIndex(i.first, i.second); -} - -QString Wallet::cachePath() const -{ - return QDir::toNativeSeparators(QString::fromStdString(m_walletImpl->filename())); -} - -QString Wallet::keysPath() const -{ - return QDir::toNativeSeparators(QString::fromStdString(m_walletImpl->keysFilename()));; -} - -void Wallet::store() -{ - m_walletImpl->store(); -} - -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){ - qDebug() << "RESTORING"; - m_walletImpl->setRecoveringFromSeed(true); - } - if (isRecoveringFromDevice){ - qDebug() << "RESTORING FROM DEVICE"; - m_walletImpl->setRecoveringFromDevice(true); - } - if (isRecovering || isRecoveringFromDevice) { - m_walletImpl->setRefreshFromBlockHeight(restoreHeight); - } - - { - QMutexLocker locker(&m_proxyMutex); - - if (!m_walletImpl->init(daemonAddress.toStdString(), upperTransactionLimit, m_daemonUsername.toStdString(), m_daemonPassword.toStdString(), m_useSSL, false, proxyAddress.toStdString())) - { - return false; - } - - m_proxyAddress = proxyAddress; - } - emit proxyAddressChanged(); - - setTrustedDaemon(trustedDaemon); - return true; -} - -void Wallet::setDaemonLogin(const QString &daemonUsername, const QString &daemonPassword) -{ - // store daemon login - m_daemonUsername = daemonUsername; - m_daemonPassword = daemonPassword; -} - -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, proxyAddress] { - bool success = init(daemonAddress, trustedDaemon, upperTransactionLimit, isRecovering, isRecoveringFromDevice, restoreHeight, proxyAddress); - if (success) - { - emit walletCreationHeightChanged(); - qDebug() << "init async finished - starting refresh"; - startRefresh(); - } - }); - if (future.first) - { - setConnectionStatus(Wallet::ConnectionStatus_Connecting); + if (this->isSynchronized()) { + this->coins()->refreshUnlocked(); + this->history()->refresh(this->currentSubaddressAccount()); + // Todo: only refresh tx confirmations } } -bool Wallet::setDaemon(const QString &daemonAddress) -{ - qDebug() << "setDaemon: " + daemonAddress; - return m_walletImpl->setDaemon(daemonAddress.toStdString(), m_daemonUsername.toStdString(), m_daemonPassword.toStdString(), m_useSSL); +void Wallet::onUpdated() { + if (m_walletImpl->synchronized()) { + this->refreshModels(); + this->storeSafer(); + } + + this->updateBalance(); } -bool Wallet::isHwBacked() const -{ +void Wallet::onRefreshed(bool success, const QString &message) { + qDebug() << "onRefreshed"; + + if (!success) { + setConnectionStatus(ConnectionStatus_Disconnected); + // Something went wrong during refresh, in some cases we need to notify the user + qCritical() << "Exception during refresh: " << message; // Can't use ->errorString() here, other SLOT might snipe it first + return; + } + + if (!this->refreshedOnce) { + this->refreshModels(); + this->refreshedOnce = true; + emit walletRefreshed(); + // store wallet immediately upon finishing synchronization + this->storeSafer(); + } +} + +void Wallet::refreshModels() { + m_history->refresh(this->currentSubaddressAccount()); + m_coins->refresh(this->currentSubaddressAccount()); + bool r = this->subaddress()->refresh(this->currentSubaddressAccount()); + + if (!r) { + // This should only happen if wallet keys got corrupted or were tampered with + // The list of subaddresses is wiped to prevent loss of funds + // Notify MainWindow to display an error message + emit keysCorrupted(); + } +} + +// #################### Hardware wallet #################### + +bool Wallet::isHwBacked() const { return m_walletImpl->getDeviceType() != Monero::Wallet::Device_Software; } -bool Wallet::isLedger() const -{ +bool Wallet::isLedger() const { return m_walletImpl->getDeviceType() == Monero::Wallet::Device_Ledger; } -bool Wallet::isTrezor() const -{ +bool Wallet::isTrezor() const { return m_walletImpl->getDeviceType() == Monero::Wallet::Device_Trezor; } -bool Wallet::reconnectDevice() -{ +bool Wallet::isDeviceConnected() const { + return m_walletImpl->isDeviceConnected(); +} + +bool Wallet::reconnectDevice() { return m_walletImpl->reconnectDevice(); } -//! create a view only wallet -bool Wallet::createViewOnly(const QString &path, const QString &password) const -{ - // Create path - QDir d = QFileInfo(path).absoluteDir(); - d.mkpath(d.absolutePath()); - return m_walletImpl->createWatchOnly(path.toStdString(),password.toStdString(),m_walletImpl->getSeedLanguage()); -} - -bool Wallet::connectToDaemon() -{ - return m_walletImpl->connectToDaemon(); -} - -void Wallet::setTrustedDaemon(bool arg) -{ - m_walletImpl->setTrustedDaemon(arg); -} - -void Wallet::setUseSSL(bool ssl) -{ - m_useSSL = ssl; -} - -bool Wallet::viewOnly() const -{ - return m_walletImpl->watchOnly(); -} - -bool Wallet::isDeterministic() const -{ - return m_walletImpl->isDeterministic(); -} - -quint64 Wallet::balance() const -{ - return balance(m_currentSubaddressAccount); -} - -quint64 Wallet::balance(quint32 accountIndex) const -{ - return m_walletImpl->balance(accountIndex); -} - -quint64 Wallet::balanceAll() const -{ - return m_walletImpl->balanceAll(); -} - -quint64 Wallet::unlockedBalance() const -{ - return unlockedBalance(m_currentSubaddressAccount); -} - -quint64 Wallet::unlockedBalance(quint32 accountIndex) const -{ - return m_walletImpl->unlockedBalance(accountIndex); -} - -quint64 Wallet::unlockedBalanceAll() const -{ - return m_walletImpl->unlockedBalanceAll(); -} - -quint32 Wallet::currentSubaddressAccount() const -{ - return m_currentSubaddressAccount; -} -void Wallet::switchSubaddressAccount(quint32 accountIndex) -{ - if (accountIndex < numSubaddressAccounts()) - { - m_currentSubaddressAccount = accountIndex; - if (!setCacheAttribute(ATTRIBUTE_SUBADDRESS_ACCOUNT, QString::number(m_currentSubaddressAccount))) - { - qWarning() << "failed to set " << ATTRIBUTE_SUBADDRESS_ACCOUNT << " cache attribute"; - } - m_subaddress->refresh(m_currentSubaddressAccount); - m_history->refresh(m_currentSubaddressAccount); - m_coins->refresh(m_currentSubaddressAccount); - this->subaddressModel()->setCurrentSubaddressAccount(m_currentSubaddressAccount); - this->coinsModel()->setCurrentSubaddressAccount(m_currentSubaddressAccount); - emit currentSubaddressAccountChanged(); +void Wallet::onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort) { + if (m_walletListener != nullptr) { + m_walletListener->onPassphraseEntered(passphrase, enter_on_device, entry_abort); } } -void Wallet::addSubaddressAccount(const QString& label) -{ - m_walletImpl->addSubaddressAccount(label.toStdString()); - switchSubaddressAccount(numSubaddressAccounts() - 1); -} -quint32 Wallet::numSubaddressAccounts() const -{ - return m_walletImpl->numSubaddressAccounts(); -} -quint32 Wallet::numSubaddresses(quint32 accountIndex) const -{ - return m_walletImpl->numSubaddresses(accountIndex); -} -void Wallet::addSubaddress(const QString& label) -{ - m_walletImpl->addSubaddress(currentSubaddressAccount(), label.toStdString()); -} -QString Wallet::getSubaddressLabel(quint32 accountIndex, quint32 addressIndex) const -{ - return QString::fromStdString(m_walletImpl->getSubaddressLabel(accountIndex, addressIndex)); -} -void Wallet::setSubaddressLabel(quint32 accountIndex, quint32 addressIndex, const QString &label) -{ - m_walletImpl->setSubaddressLabel(accountIndex, addressIndex, label.toStdString()); - emit currentSubaddressAccountChanged(); -} -void Wallet::deviceShowAddressAsync(quint32 accountIndex, quint32 addressIndex, const QString &paymentId) -{ - m_scheduler.run([this, accountIndex, addressIndex, paymentId] { - m_walletImpl->deviceShowAddress(accountIndex, addressIndex, paymentId.toStdString()); - emit deviceShowAddressShowed(); - }); + +void Wallet::onWalletPassphraseNeeded(bool on_device) { + emit this->walletPassphraseNeeded(on_device); } -QString Wallet::getSubaddressLookahead() const -{ - auto lookahead = m_walletImpl->getSubaddressLookahead(); +// #################### Import / Export #################### - return QString("%1:%2").arg(QString::number(lookahead.first), QString::number(lookahead.second)); -} - -bool Wallet::refreshHeights() -{ - // daemonHeight and targetHeight will be 0 if call to get_info fails - - quint64 daemonHeight; - QPair> daemonHeightFuture = m_scheduler.run([this, &daemonHeight] { - daemonHeight = m_walletImpl->daemonBlockChainHeight();; - }); - if (!daemonHeightFuture.first) - { - return false; - } - // We must wait here for get_info to return, otherwise targetHeight will get cache value of 0 - daemonHeightFuture.second.waitForFinished(); - bool success = daemonHeight > 0; - - quint64 targetHeight = 0; - if (success) { - QPair> targetHeightFuture = m_scheduler.run([this, &targetHeight] { - targetHeight = m_walletImpl->daemonBlockChainTargetHeight(); - }); - if (!targetHeightFuture.first) - { - return false; - } - targetHeightFuture.second.waitForFinished(); - } - - m_daemonBlockChainHeight = daemonHeight; - m_daemonBlockChainTargetHeight = targetHeight; - - success = (daemonHeight > 0 && targetHeight > 0); - - if (success) { - quint64 walletHeight = blockChainHeight(); - emit heightRefreshed(walletHeight, daemonHeight, targetHeight); - - if (walletHeight < (daemonHeight - 1)) { - setConnectionStatus(ConnectionStatus_Synchronizing); - } else { - setConnectionStatus(ConnectionStatus_Synchronized); - } - } else { - setConnectionStatus(ConnectionStatus_Disconnected); - } - - return success; -} - -quint64 Wallet::blockChainHeight() const -{ - return m_walletImpl->blockChainHeight(); -} - -quint64 Wallet::daemonBlockChainHeight() const -{ - // Can not block UI - return m_daemonBlockChainHeight; -} - -quint64 Wallet::daemonBlockChainTargetHeight() const -{ - // Can not block UI - return m_daemonBlockChainTargetHeight; -} - -bool Wallet::exportKeyImages(const QString& path, bool all) -{ +bool Wallet::exportKeyImages(const QString& path, bool all) { return m_walletImpl->exportKeyImages(path.toStdString(), all); } -bool Wallet::importKeyImages(const QString& path) -{ +bool Wallet::importKeyImages(const QString& path) { return m_walletImpl->importKeyImages(path.toStdString()); } @@ -507,298 +530,344 @@ bool Wallet::importTransaction(const QString& txid) { return m_walletImpl->scanTransactions(txids); } -QString Wallet::printBlockchain() -{ +// #################### Wallet cache #################### + +void Wallet::store() { + m_walletImpl->store(); +} + +void Wallet::storeSafer() { + // Do not store a synchronizing wallet: store() is NOT thread safe and may crash the wallet + if (!this->isSynchronized()) { + return; + } + + qDebug() << "Storing wallet"; + this->store(); +} + +QString Wallet::cachePath() const { + return QDir::toNativeSeparators(QString::fromStdString(m_walletImpl->filename())); +} + +QString Wallet::keysPath() const { + return QDir::toNativeSeparators(QString::fromStdString(m_walletImpl->keysFilename()));; +} + +bool Wallet::setPassword(const QString &oldPassword, const QString &newPassword) { + return m_walletImpl->setPassword(oldPassword.toStdString(), newPassword.toStdString()); +} + +bool Wallet::verifyPassword(const QString &password) { + return m_walletImpl->verifyPassword(password.toStdString()); +} + +bool Wallet::cacheAttributeExists(const QString &key) { + return m_walletImpl->cacheAttributeExists(key.toStdString()); +} + +bool Wallet::setCacheAttribute(const QString &key, const QString &val) { + return m_walletImpl->setCacheAttribute(key.toStdString(), val.toStdString()); +} + +QString Wallet::getCacheAttribute(const QString &key) const { + return QString::fromStdString(m_walletImpl->getCacheAttribute(key.toStdString())); +} + +void Wallet::addCacheTransaction(const QString &txid, const QString &txHex) { + this->setCacheAttribute(QString("tx:%1").arg(txid), txHex); +} + +QString Wallet::getCacheTransaction(const QString &txid) const { + return this->getCacheAttribute(QString("tx:%1").arg(txid)); +} + +bool Wallet::setUserNote(const QString &txid, const QString ¬e) { + return m_walletImpl->setUserNote(txid.toStdString(), note.toStdString()); +} + +QString Wallet::getUserNote(const QString &txid) const { + return QString::fromStdString(m_walletImpl->getUserNote(txid.toStdString())); +} + +QString Wallet::printBlockchain() { return QString::fromStdString(m_walletImpl->printBlockchain()); } -QString Wallet::printTransfers() -{ +QString Wallet::printTransfers() { return QString::fromStdString(m_walletImpl->printTransfers()); } -QString Wallet::printPayments() -{ +QString Wallet::printPayments() { return QString::fromStdString(m_walletImpl->printPayments()); } -QString Wallet::printUnconfirmedPayments() -{ +QString Wallet::printUnconfirmedPayments() { return QString::fromStdString(m_walletImpl->printUnconfirmedPayments()); } -QString Wallet::printConfirmedTransferDetails() -{ +QString Wallet::printConfirmedTransferDetails() { return QString::fromStdString(m_walletImpl->printConfirmedTransferDetails()); } -QString Wallet::printUnconfirmedTransferDetails() -{ +QString Wallet::printUnconfirmedTransferDetails() { return QString::fromStdString(m_walletImpl->printUnconfirmedTransferDetails()); } -QString Wallet::printPubKeys() -{ +QString Wallet::printPubKeys() { return QString::fromStdString(m_walletImpl->printPubKeys()); } -QString Wallet::printTxNotes() -{ +QString Wallet::printTxNotes() { return QString::fromStdString(m_walletImpl->printTxNotes()); } -QString Wallet::printSubaddresses() -{ +QString Wallet::printSubaddresses() { return QString::fromStdString(m_walletImpl->printSubaddresses()); } -QString Wallet::printSubaddressLabels() -{ +QString Wallet::printSubaddressLabels() { return QString::fromStdString(m_walletImpl->printSubaddressLabels()); } -QString Wallet::printAdditionalTxKeys() -{ +QString Wallet::printAdditionalTxKeys() { return QString::fromStdString(m_walletImpl->printAdditionalTxKeys()); } -QString Wallet::printAttributes() -{ +QString Wallet::printAttributes() { return QString::fromStdString(m_walletImpl->printAttributes()); } -QString Wallet::printKeyImages() -{ +QString Wallet::printKeyImages() { return QString::fromStdString(m_walletImpl->printKeyImages()); } -QString Wallet::printAccountTags() -{ +QString Wallet::printAccountTags() { return QString::fromStdString(m_walletImpl->printAccountTags()); } -QString Wallet::printTxKeys() -{ +QString Wallet::printTxKeys() { return QString::fromStdString(m_walletImpl->printTxKeys()); } -QString Wallet::printAddressBook() -{ +QString Wallet::printAddressBook() { return QString::fromStdString(m_walletImpl->printAddressBook()); } -QString Wallet::printScannedPoolTxs() -{ +QString Wallet::printScannedPoolTxs() { return QString::fromStdString(m_walletImpl->printScannedPoolTxs()); } +// #################### Transactions #################### + +// Phase 0: Pre-construction setup + +void Wallet::setSelectedInputs(const QStringList &selectedInputs) { + m_selectedInputs.clear(); + for (const auto &input : selectedInputs) { + m_selectedInputs.insert(input.toStdString()); + } + emit selectedInputsChanged(selectedInputs); +} + +// Phase 1: Transaction creation +// Pick one: +// - createTransaction +// - createTransactionMultiDest +// - sweepOutputs + +void Wallet::createTransaction(const QString &address, quint64 amount, const QString &description, bool all) { + this->tmpTxDescription = description; + + if (!all && amount == 0) { + emit createTransactionError("Cannot send nothing"); + return; + } + + quint64 unlocked_balance = this->unlockedBalance(); + if (!all && amount > unlocked_balance) { + emit createTransactionError(QString("Not enough money to spend.\n\n" + "Spendable balance: %1").arg(WalletManager::displayAmount(unlocked_balance))); + return; + } else if (unlocked_balance == 0) { + emit createTransactionError("No money to spend"); + return; + } + + qInfo() << "Creating transaction"; + + m_scheduler.run([this, all, address, amount] { + std::set subaddr_indices; + + Monero::PendingTransaction * ptImpl = m_walletImpl->createTransaction(address.toStdString(), "", all ? Monero::optional() : Monero::optional(amount), constants::mixin, + static_cast(this->tx_priority), + currentSubaddressAccount(), subaddr_indices, m_selectedInputs); + PendingTransaction *tx = new PendingTransaction(ptImpl, this); + + QVector addresses{address}; + emit transactionCreated(tx, addresses); + }); + + emit initiateTransaction(); +} + +void Wallet::createTransactionMultiDest(const QVector &addresses, const QVector &amounts, const QString &description) { + this->tmpTxDescription = description; + + quint64 total_amount = 0; + for (auto &amount : amounts) { + total_amount += amount; + } + + auto unlocked_balance = this->unlockedBalance(); + if (total_amount > unlocked_balance) { + emit createTransactionError("Not enough money to spend"); + } + + qInfo() << "Creating transaction"; + m_scheduler.run([this, addresses, amounts] { + std::vector dests; + for (auto &addr : addresses) { + dests.push_back(addr.toStdString()); + } + + std::vector amount; + for (auto &a : amounts) { + amount.push_back(a); + } + + std::set subaddr_indices; + Monero::PendingTransaction *ptImpl = m_walletImpl->createTransactionMultDest(dests, "", amount, constants::mixin, + static_cast(this->tx_priority), + currentSubaddressAccount(), subaddr_indices, m_selectedInputs); + PendingTransaction *tx = new PendingTransaction(ptImpl); + emit transactionCreated(tx, addresses); + }); + + emit initiateTransaction(); +} + +void Wallet::sweepOutputs(const QVector &keyImages, QString address, bool churn, int outputs) { + if (churn) { + address = this->address(0, 0); + } + + qInfo() << "Creating transaction"; + m_scheduler.run([this, keyImages, address, outputs] { + std::vector kis; + for (const auto &key_image : keyImages) { + kis.push_back(key_image.toStdString()); + } + Monero::PendingTransaction *ptImpl = m_walletImpl->createTransactionSelected(kis, address.toStdString(), outputs, static_cast(this->tx_priority)); + PendingTransaction *tx = new PendingTransaction(ptImpl, this); + + QVector addresses {address}; + emit transactionCreated(tx, addresses); + }); + + emit initiateTransaction(); +} + +// Phase 2: Transaction construction completed + +void Wallet::onCreateTransactionError(const QString &msg) { + this->tmpTxDescription = ""; + emit endTransaction(); +} + +void Wallet::onTransactionCreated(PendingTransaction *tx, const QVector &address) { + qDebug() << Q_FUNC_INFO; + + for (auto &addr : address) { + if (addr == constants::donationAddress) { + this->donationSending = true; + } + } + + // Let UI know that the transaction was constructed + emit endTransaction(); + + // tx created, but not sent yet. ask user to verify first. + emit createTransactionSuccess(tx, address); +} + +// Phase 3: Commit or dispose + +void Wallet::commitTransaction(PendingTransaction *tx, const QString &description) { + // Clear list of selected transfers + this->setSelectedInputs({}); + + // Nodes - even well-connected, properly configured ones - consistently fail to relay transactions + // To mitigate transactions failing we just send the transaction to every node we know about over Tor + if (config()->get(Config::multiBroadcast).toBool()) { + // Let MainWindow handle this + emit multiBroadcast(tx); + } + + m_scheduler.run([this, tx, description] { + auto txIdList = tx->txid(); // retrieve before commit + bool success = tx->commit(); + + if (success && !description.isEmpty()) { + for (const auto &txid : txIdList) { + this->setUserNote(txid, description); + } + } + + // Store wallet immediately, so we don't risk losing tx key if wallet crashes + this->storeSafer(); + + this->history()->refresh(this->currentSubaddressAccount()); + this->coins()->refresh(this->currentSubaddressAccount()); + + this->updateBalance(); + + // this tx was a donation to Feather, stop our nagging + if (this->donationSending) { + this->donationSending = false; + emit donationSent(); + } + + emit transactionCommitted(success, tx, txIdList); + }); +} + +void Wallet::disposeTransaction(PendingTransaction *t) { + m_walletImpl->disposeTransaction(t->m_pimpl); + delete t; +} + +void Wallet::disposeTransaction(UnsignedTransaction *t) { + delete t; +} + +// #################### Transaction import #################### + bool Wallet::haveTransaction(const QString &txid) { return m_walletImpl->haveTransaction(txid.toStdString()); } -void Wallet::startRefresh() -{ - m_refreshEnabled = true; - m_refreshNow = true; -} - -void Wallet::pauseRefresh() -{ - m_refreshEnabled = false; -} - -PendingTransaction *Wallet::createTransaction(const QString &dst_addr, const QString &payment_id, - quint64 amount, quint32 mixin_count, - PendingTransaction::Priority priority, const QStringList &preferredInputs) -{ -// pauseRefresh(); - std::set preferred_inputs; - for (const auto &input : preferredInputs) { - preferred_inputs.insert(input.toStdString()); - } - - std::set subaddr_indices; - Monero::PendingTransaction * ptImpl = m_walletImpl->createTransaction( - dst_addr.toStdString(), payment_id.toStdString(), amount, mixin_count, - static_cast(priority), currentSubaddressAccount(), subaddr_indices, preferred_inputs); - PendingTransaction * result = new PendingTransaction(ptImpl, nullptr); - -// startRefresh(); - return result; -} - -void Wallet::createTransactionAsync(const QString &dst_addr, const QString &payment_id, - quint64 amount, quint32 mixin_count, - PendingTransaction::Priority priority, const QStringList &preferredInputs) -{ - m_scheduler.run([this, dst_addr, payment_id, amount, mixin_count, priority, preferredInputs] { - PendingTransaction *tx = createTransaction(dst_addr, payment_id, amount, mixin_count, priority, preferredInputs); - QVector address {dst_addr}; - emit transactionCreated(tx, address); - }); -} - -PendingTransaction* Wallet::createTransactionMultiDest(const QVector &dst_addr, const QVector &amount, - PendingTransaction::Priority priority, const QStringList &preferredInputs) -{ -// pauseRefresh(); - - std::vector dests; - for (auto &addr : dst_addr) { - dests.push_back(addr.toStdString()); - } - - std::vector amounts; - for (auto &a : amount) { - amounts.push_back(a); - } - - std::set preferred_inputs; - for (const auto &input : preferredInputs) { - preferred_inputs.insert(input.toStdString()); - } - - // TODO: remove mixin count - std::set subaddr_indices; - Monero::PendingTransaction * ptImpl = m_walletImpl->createTransactionMultDest(dests, "", amounts, 11, static_cast(priority), currentSubaddressAccount(), subaddr_indices, preferred_inputs); - PendingTransaction * result = new PendingTransaction(ptImpl); - -// startRefresh(); - return result; -} - -void Wallet::createTransactionMultiDestAsync(const QVector &dst_addr, const QVector &amount, - PendingTransaction::Priority priority, const QStringList &preferredInputs) -{ - m_scheduler.run([this, dst_addr, amount, priority, preferredInputs] { - PendingTransaction *tx = createTransactionMultiDest(dst_addr, amount, priority, preferredInputs); - QVector addresses; - for (auto &addr : dst_addr) { - addresses.push_back(addr); - } - emit transactionCreated(tx, addresses); - }); -} - -PendingTransaction *Wallet::createTransactionAll(const QString &dst_addr, const QString &payment_id, - quint32 mixin_count, PendingTransaction::Priority priority, - const QStringList &preferredInputs) -{ -// pauseRefresh(); - - std::set preferred_inputs; - for (const auto &input : preferredInputs) { - preferred_inputs.insert(input.toStdString()); - } - - std::set subaddr_indices; - Monero::PendingTransaction * ptImpl = m_walletImpl->createTransaction( - dst_addr.toStdString(), payment_id.toStdString(), Monero::optional(), mixin_count, - static_cast(priority), currentSubaddressAccount(), subaddr_indices, preferred_inputs); - PendingTransaction * result = new PendingTransaction(ptImpl, this); - -// startRefresh(); - return result; -} - -void Wallet::createTransactionAllAsync(const QString &dst_addr, const QString &payment_id, - quint32 mixin_count, - PendingTransaction::Priority priority, const QStringList &preferredInputs) -{ - m_scheduler.run([this, dst_addr, payment_id, mixin_count, priority, preferredInputs] { - PendingTransaction *tx = createTransactionAll(dst_addr, payment_id, mixin_count, priority, preferredInputs); - QVector address {dst_addr}; - emit transactionCreated(tx, address); - }); -} - -PendingTransaction *Wallet::createTransactionSingle(const QString &key_image, const QString &dst_addr, const size_t outputs, - PendingTransaction::Priority priority) -{ -// pauseRefresh(); - - Monero::PendingTransaction * ptImpl = m_walletImpl->createTransactionSingle(key_image.toStdString(), dst_addr.toStdString(), - outputs, static_cast(priority)); - PendingTransaction * result = new PendingTransaction(ptImpl, this); - -// startRefresh(); - return result; -} - -void Wallet::createTransactionSingleAsync(const QString &key_image, const QString &dst_addr, const size_t outputs, - PendingTransaction::Priority priority) -{ - m_scheduler.run([this, key_image, dst_addr, outputs, priority] { - PendingTransaction *tx = createTransactionSingle(key_image, dst_addr, outputs, priority); - QVector address {dst_addr}; - emit transactionCreated(tx, address); - }); -} - -PendingTransaction *Wallet::createTransactionSelected(const QVector &key_images, const QString &dst_addr, - size_t outputs, PendingTransaction::Priority priority) -{ - std::vector kis; - for (const auto &key_image : key_images) { - kis.push_back(key_image.toStdString()); - } - Monero::PendingTransaction *ptImpl = m_walletImpl->createTransactionSelected(kis, dst_addr.toStdString(), outputs, static_cast(priority)); - PendingTransaction *result = new PendingTransaction(ptImpl, this); - - return result; -} - -void Wallet::createTransactionSelectedAsync(const QVector &key_images, const QString &dst_addr, - size_t outputs, PendingTransaction::Priority priority) -{ - m_scheduler.run([this, key_images, dst_addr, outputs, priority] { - PendingTransaction *tx = createTransactionSelected(key_images, dst_addr, outputs, priority); - QVector address {dst_addr}; - emit transactionCreated(tx, address); - }); -} - -PendingTransaction *Wallet::createSweepUnmixableTransaction() -{ -// pauseRefresh(); - - Monero::PendingTransaction * ptImpl = m_walletImpl->createSweepUnmixableTransaction(); - PendingTransaction * result = new PendingTransaction(ptImpl, this); - -// startRefresh(); - return result; -} - -void Wallet::createSweepUnmixableTransactionAsync() -{ - m_scheduler.run([this] { - PendingTransaction *tx = createSweepUnmixableTransaction(); - QVector address {""}; - emit transactionCreated(tx, address); - }); -} - UnsignedTransaction * Wallet::loadTxFile(const QString &fileName) { qDebug() << "Trying to sign " << fileName; - Monero::UnsignedTransaction * ptImpl = m_walletImpl->loadUnsignedTx(fileName.toStdString()); - UnsignedTransaction * result = new UnsignedTransaction(ptImpl, m_walletImpl, this); + Monero::UnsignedTransaction *ptImpl = m_walletImpl->loadUnsignedTx(fileName.toStdString()); + UnsignedTransaction *result = new UnsignedTransaction(ptImpl, m_walletImpl, this); return result; } UnsignedTransaction * Wallet::loadTxFromBase64Str(const QString &unsigned_tx) { - Monero::UnsignedTransaction * ptImpl = m_walletImpl->loadUnsignedTxFromBase64Str(unsigned_tx.toStdString()); - UnsignedTransaction * result = new UnsignedTransaction(ptImpl, m_walletImpl, this); + Monero::UnsignedTransaction *ptImpl = m_walletImpl->loadUnsignedTxFromBase64Str(unsigned_tx.toStdString()); + UnsignedTransaction *result = new UnsignedTransaction(ptImpl, m_walletImpl, this); return result; } PendingTransaction * Wallet::loadSignedTxFile(const QString &fileName) { qDebug() << "Tying to load " << fileName; - Monero::PendingTransaction * ptImpl = m_walletImpl->loadSignedTx(fileName.toStdString()); - PendingTransaction * result = new PendingTransaction(ptImpl, this); + Monero::PendingTransaction *ptImpl = m_walletImpl->loadSignedTx(fileName.toStdString()); + PendingTransaction *result = new PendingTransaction(ptImpl, this); return result; } @@ -811,77 +880,16 @@ bool Wallet::submitTxFile(const QString &fileName) const return m_walletImpl->importKeyImages(fileName.toStdString() + "_keyImages"); } -bool Wallet::refresh(bool historyAndSubaddresses /* = true */) -{ - refreshingSet(true); - const auto cleanup = sg::make_scope_guard([this]() noexcept { - refreshingSet(false); - }); +// #################### Models #################### - { - QMutexLocker locker(&m_asyncMutex); - - bool result = m_walletImpl->refresh(); - if (historyAndSubaddresses) - { - m_history->refresh(currentSubaddressAccount()); - m_subaddress->refresh(currentSubaddressAccount()); - m_subaddressAccount->getAll(); - } - - return result; - } -} - -void Wallet::commitTransactionAsync(PendingTransaction *t, const QString &description) -{ - m_scheduler.run([this, t, description] { - auto txIdList = t->txid(); // retrieve before commit - bool success = t->commit(); - - if (success && !description.isEmpty()) { - for (const auto &txid : txIdList) { - this->setUserNote(txid, description); - } - } - - emit transactionCommitted(success, t, txIdList); - }); -} - -void Wallet::disposeTransaction(PendingTransaction *t) -{ - m_walletImpl->disposeTransaction(t->m_pimpl); - delete t; -} - -void Wallet::disposeTransaction(UnsignedTransaction *t) -{ - delete t; -} - -//void Wallet::estimateTransactionFeeAsync(const QString &destination, -// quint64 amount, -// PendingTransaction::Priority priority, -// const QJSValue &callback) -//{ -// m_scheduler.run([this, destination, amount, priority] { -// const uint64_t fee = m_walletImpl->estimateTransactionFee( -// {std::make_pair(destination.toStdString(), amount)}, -// static_cast(priority)); -// return QJSValueList({QString::fromStdString(Monero::Wallet::displayAmount(fee))}); -// }, callback); -//} - -TransactionHistory *Wallet::history() const -{ +TransactionHistory *Wallet::history() const { return m_history; } -TransactionHistoryProxyModel *Wallet::historyModel() const +TransactionHistoryProxyModel *Wallet::historyModel() { if (!m_historyModel) { - Wallet * w = const_cast(this); + Wallet *w = const_cast(this); m_historyModel = new TransactionHistoryModel(w); m_historyModel->setTransactionHistory(this->history()); m_historySortFilterModel = new TransactionHistoryProxyModel(w); @@ -893,108 +901,49 @@ TransactionHistoryProxyModel *Wallet::historyModel() const return m_historySortFilterModel; } -TransactionHistoryModel *Wallet::transactionHistoryModel() const -{ +TransactionHistoryModel* Wallet::transactionHistoryModel() const { return m_historyModel; } -AddressBook *Wallet::addressBook() const -{ +AddressBook* Wallet::addressBook() const { return m_addressBook; } -AddressBookModel *Wallet::addressBookModel() const -{ - - if (!m_addressBookModel) { - Wallet * w = const_cast(this); - m_addressBookModel = new AddressBookModel(w,m_addressBook); - } - +AddressBookModel* Wallet::addressBookModel() const { return m_addressBookModel; } -Subaddress *Wallet::subaddress() -{ +Subaddress* Wallet::subaddress() const { return m_subaddress; } -SubaddressModel *Wallet::subaddressModel() -{ - if (!m_subaddressModel) { - m_subaddressModel = new SubaddressModel(this, m_subaddress); - } +SubaddressModel* Wallet::subaddressModel() const { return m_subaddressModel; } -SubaddressAccount *Wallet::subaddressAccount() const -{ +SubaddressAccount* Wallet::subaddressAccount() const { return m_subaddressAccount; } -SubaddressAccountModel *Wallet::subaddressAccountModel() const -{ - if (!m_subaddressAccountModel) { - Wallet * w = const_cast(this); - m_subaddressAccountModel = new SubaddressAccountModel(w,m_subaddressAccount); - } +SubaddressAccountModel* Wallet::subaddressAccountModel() const { return m_subaddressAccountModel; } -Coins *Wallet::coins() const -{ +Coins* Wallet::coins() const { return m_coins; } -CoinsModel *Wallet::coinsModel() const -{ - if (!m_coinsModel) { - Wallet * w = const_cast(this); - m_coinsModel = new CoinsModel(w, m_coins); - } +CoinsModel* Wallet::coinsModel() const { return m_coinsModel; } -QString Wallet::generatePaymentId() const -{ - return QString::fromStdString(Monero::Wallet::genPaymentId()); -} +// #################### Transaction proofs #################### -QString Wallet::integratedAddress(const QString &paymentId) const -{ - return QString::fromStdString(m_walletImpl->integratedAddress(paymentId.toStdString())); -} - -bool Wallet::cacheAttributeExists(const QString &key) { - return m_walletImpl->cacheAttributeExists(key.toStdString()); -} - -QString Wallet::getCacheAttribute(const QString &key) const { - return QString::fromStdString(m_walletImpl->getCacheAttribute(key.toStdString())); -} - -bool Wallet::setCacheAttribute(const QString &key, const QString &val) -{ - return m_walletImpl->setCacheAttribute(key.toStdString(), val.toStdString()); -} - -bool Wallet::setUserNote(const QString &txid, const QString ¬e) -{ - return m_walletImpl->setUserNote(txid.toStdString(), note.toStdString()); -} - -QString Wallet::getUserNote(const QString &txid) const -{ - return QString::fromStdString(m_walletImpl->getUserNote(txid.toStdString())); -} - -QString Wallet::getTxKey(const QString &txid) const -{ +QString Wallet::getTxKey(const QString &txid) const { return QString::fromStdString(m_walletImpl->getTxKey(txid.toStdString())); } -void Wallet::getTxKeyAsync(const QString &txid, const std::function &callback) -{ +void Wallet::getTxKeyAsync(const QString &txid, const std::function &callback) { m_scheduler.run([this, txid] { QVariantMap map; map["tx_key"] = getTxKey(txid); @@ -1002,32 +951,23 @@ void Wallet::getTxKeyAsync(const QString &txid, const std::functioncheckTxKey(txid.toStdString(), tx_key.toStdString(), address.toStdString(), received, in_pool, confirmations); QString errorString = success ? "" : this->errorString(); bool good = received > 0; return {success, good, QString::fromStdString(Monero::Wallet::displayAmount(received)), in_pool, confirmations, errorString}; } -TxProof Wallet::getTxProof(const QString &txid, const QString &address, const QString &message) const -{ +TxProof Wallet::getTxProof(const QString &txid, const QString &address, const QString &message) const { std::string result = m_walletImpl->getTxProof(txid.toStdString(), address.toStdString(), message.toStdString()); return TxProof(QString::fromStdString(result), QString::fromStdString(m_walletImpl->errorString())); } -//void Wallet::getTxProofAsync(const QString &txid, const QString &address, const QString &message, const QJSValue &callback) -//{ -// m_scheduler.run([this, txid, address, message] { -// return QJSValueList({txid, getTxProof(txid, address, message)}); -// }, callback); -//} - -TxProofResult Wallet::checkTxProof(const QString &txid, const QString &address, const QString &message, const QString &signature) -{ +TxProofResult Wallet::checkTxProof(const QString &txid, const QString &address, const QString &message, const QString &signature) { bool good; uint64_t received; bool in_pool; @@ -1036,29 +976,19 @@ TxProofResult Wallet::checkTxProof(const QString &txid, const QString &address, return {success, good, received, in_pool, confirmations}; } -void Wallet::checkTxProofAsync(const QString &txid, const QString &address, const QString &message, - const QString &signature) { +void Wallet::checkTxProofAsync(const QString &txid, const QString &address, const QString &message, const QString &signature) { m_scheduler.run([this, txid, address, message, signature] { auto result = this->checkTxProof(txid, address, message, signature); emit transactionProofVerified(result); }); } -Q_INVOKABLE TxProof Wallet::getSpendProof(const QString &txid, const QString &message) const -{ +TxProof Wallet::getSpendProof(const QString &txid, const QString &message) const { std::string result = m_walletImpl->getSpendProof(txid.toStdString(), message.toStdString()); return TxProof(QString::fromStdString(result), QString::fromStdString(m_walletImpl->errorString())); } -//void Wallet::getSpendProofAsync(const QString &txid, const QString &message, const QJSValue &callback) -//{ -// m_scheduler.run([this, txid, message] { -// return QJSValueList({txid, getSpendProof(txid, message)}); -// }, callback); -//} - -Q_INVOKABLE QPair Wallet::checkSpendProof(const QString &txid, const QString &message, - const QString &signature) const { +QPair Wallet::checkSpendProof(const QString &txid, const QString &message, const QString &signature) const { bool good; bool success = m_walletImpl->checkSpendProof(txid.toStdString(), message.toStdString(), signature.toStdString(), good); return {success, good}; @@ -1071,80 +1001,81 @@ void Wallet::checkSpendProofAsync(const QString &txid, const QString &message, c }); } -QString Wallet::signMessage(const QString &message, bool filename, const QString &address) const -{ - if (filename) { - QFile file(message); - uchar *data = NULL; +// #################### Sign / Verify message #################### - try { - if (!file.open(QIODevice::ReadOnly)) - return ""; - quint64 size = file.size(); - if (size == 0) { - file.close(); - return QString::fromStdString(m_walletImpl->signMessage(std::string())); - } - data = file.map(0, size); - if (!data) { - file.close(); - return ""; - } - std::string signature = m_walletImpl->signMessage(std::string(reinterpret_cast(data), size), address.toStdString()); - file.unmap(data); - file.close(); - return QString::fromStdString(signature); +QString Wallet::signMessage(const QString &message, bool filename, const QString &address) const { + if (filename) { + QFile file(message); + uchar *data = nullptr; + + try { + if (!file.open(QIODevice::ReadOnly)) + return ""; + quint64 size = file.size(); + if (size == 0) { + file.close(); + return QString::fromStdString(m_walletImpl->signMessage(std::string())); + } + data = file.map(0, size); + if (!data) { + file.close(); + return ""; + } + std::string signature = m_walletImpl->signMessage(std::string(reinterpret_cast(data), size), address.toStdString()); + file.unmap(data); + file.close(); + return QString::fromStdString(signature); + } + catch (const std::exception &e) { + if (data) + file.unmap(data); + file.close(); + return ""; + } } - catch (const std::exception &e) { - if (data) - file.unmap(data); - file.close(); - return ""; + else { + return QString::fromStdString(m_walletImpl->signMessage(message.toStdString(), address.toStdString())); } - } - else { - return QString::fromStdString(m_walletImpl->signMessage(message.toStdString(), address.toStdString())); - } } -bool Wallet::verifySignedMessage(const QString &message, const QString &address, const QString &signature, bool filename) const -{ - if (filename) { - QFile file(message); - uchar *data = NULL; +bool Wallet::verifySignedMessage(const QString &message, const QString &address, const QString &signature, bool filename) const { + if (filename) { + QFile file(message); + uchar *data = nullptr; - try { - if (!file.open(QIODevice::ReadOnly)) - return false; - quint64 size = file.size(); - if (size == 0) { - file.close(); - return m_walletImpl->verifySignedMessage(std::string(), address.toStdString(), signature.toStdString()); - } - data = file.map(0, size); - if (!data) { - file.close(); - return false; - } - bool ret = m_walletImpl->verifySignedMessage(std::string(reinterpret_cast(data), size), address.toStdString(), signature.toStdString()); - file.unmap(data); - file.close(); - return ret; + try { + if (!file.open(QIODevice::ReadOnly)) + return false; + quint64 size = file.size(); + if (size == 0) { + file.close(); + return m_walletImpl->verifySignedMessage(std::string(), address.toStdString(), signature.toStdString()); + } + data = file.map(0, size); + if (!data) { + file.close(); + return false; + } + bool ret = m_walletImpl->verifySignedMessage(std::string(reinterpret_cast(data), size), address.toStdString(), signature.toStdString()); + file.unmap(data); + file.close(); + return ret; + } + catch (const std::exception &e) { + if (data) + file.unmap(data); + file.close(); + return false; + } } - catch (const std::exception &e) { - if (data) - file.unmap(data); - file.close(); - return false; + else { + return m_walletImpl->verifySignedMessage(message.toStdString(), address.toStdString(), signature.toStdString()); } - } - else { - 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 &unknown_parameters, QString &error) const -{ +// #################### URI Parsing #################### + +bool Wallet::parse_uri(const QString &uri, QString &address, QString &payment_id, uint64_t &amount, QString &tx_description, QString &recipient_name, QVector &unknown_parameters, QString &error) const { std::string s_address, s_payment_id, s_tx_description, s_recipient_name, s_error; std::vector s_unknown_parameters; bool res= m_walletImpl->parse_uri(uri.toStdString(), s_address, s_payment_id, amount, s_tx_description, s_recipient_name, s_unknown_parameters, s_error); @@ -1161,8 +1092,7 @@ bool Wallet::parse_uri(const QString &uri, QString &address, QString &payment_id return res; } -QVariantMap Wallet::parse_uri_to_object(const QString &uri) const -{ +QVariantMap Wallet::parse_uri_to_object(const QString &uri) const { QString address; QString payment_id; uint64_t amount = 0; @@ -1185,157 +1115,13 @@ QVariantMap Wallet::parse_uri_to_object(const QString &uri) const return result; } -QString Wallet::make_uri(const QString &address, quint64 &amount, const QString &description, - const QString &recipient) const -{ +QString Wallet::make_uri(const QString &address, quint64 &amount, const QString &description, const QString &recipient) const { std::string error; std::string uri = m_walletImpl->make_uri(address.toStdString(), "", amount, description.toStdString(), recipient.toStdString(), error); return QString::fromStdString(uri); } -bool Wallet::rescanSpent() -{ - QMutexLocker locker(&m_asyncMutex); - - return m_walletImpl->rescanSpent(); -} - -bool Wallet::useForkRules(quint8 required_version, quint64 earlyBlocks) const -{ - if(m_connectionStatus == Wallet::ConnectionStatus_Disconnected) - return false; - try { - return m_walletImpl->useForkRules(required_version,earlyBlocks); - } catch (const std::exception &e) { - qDebug() << e.what(); - return false; - } -} - -void Wallet::setWalletCreationHeight(quint64 height) -{ - m_walletImpl->setRefreshFromBlockHeight(height); - emit walletCreationHeightChanged(); -} - -QString Wallet::getDaemonLogPath() const -{ - return QString::fromStdString(m_walletImpl->getDefaultDataDir()) + "/bitmonero.log"; -} - -bool Wallet::blackballOutput(const QString &amount, const QString &offset) -{ - return m_walletImpl->blackballOutput(amount.toStdString(), offset.toStdString()); -} - -bool Wallet::blackballOutputs(const QList &pubkeys, bool add) -{ - std::vector std_pubkeys; - foreach (const QString &pubkey, pubkeys) { - std_pubkeys.push_back(pubkey.toStdString()); - } - return m_walletImpl->blackballOutputs(std_pubkeys, add); -} - -bool Wallet::blackballOutputs(const QString &filename, bool add) -{ - QFile file(filename); - - try { - if (!file.open(QIODevice::ReadOnly)) - return false; - QList outputs; - QTextStream in(&file); - while (!in.atEnd()) { - outputs.push_back(in.readLine()); - } - file.close(); - return blackballOutputs(outputs, add); - } - catch (const std::exception &e) { - file.close(); - return false; - } -} - -bool Wallet::unblackballOutput(const QString &amount, const QString &offset) -{ - return m_walletImpl->unblackballOutput(amount.toStdString(), offset.toStdString()); -} - -QString Wallet::getRing(const QString &key_image) -{ - std::vector cring; - if (!m_walletImpl->getRing(key_image.toStdString(), cring)) - return ""; - QString ring = ""; - for (uint64_t out: cring) - { - if (!ring.isEmpty()) - ring = ring + " "; - QString s; - s.setNum(out); - ring = ring + s; - } - return ring; -} - -QString Wallet::getRings(const QString &txid) -{ - std::vector>> crings; - if (!m_walletImpl->getRings(txid.toStdString(), crings)) - return ""; - QString ring = ""; - for (const auto &cring: crings) - { - if (!ring.isEmpty()) - ring = ring + "|"; - ring = ring + QString::fromStdString(cring.first) + " absolute"; - for (uint64_t out: cring.second) - { - ring = ring + " "; - QString s; - s.setNum(out); - ring = ring + s; - } - } - return ring; -} - -bool Wallet::setRing(const QString &key_image, const QString &ring, bool relative) -{ - std::vector cring; - QStringList strOuts = ring.split(" "); - foreach(QString str, strOuts) - { - uint64_t out; - bool ok; - out = str.toULong(&ok); - if (ok) - cring.push_back(out); - } - return m_walletImpl->setRing(key_image.toStdString(), cring, relative); -} - -void Wallet::segregatePreForkOutputs(bool segregate) -{ - m_walletImpl->segregatePreForkOutputs(segregate); -} - -void Wallet::segregationHeight(quint64 height) -{ - m_walletImpl->segregationHeight(height); -} - -void Wallet::keyReuseMitigation2(bool mitigation) -{ - m_walletImpl->keyReuseMitigation2(mitigation); -} - -void Wallet::onWalletPassphraseNeeded(bool on_device) -{ - emit this->walletPassphraseNeeded(on_device); -} +// #################### Misc / Unused #################### quint64 Wallet::getBytesReceived() const { // TODO: this can segfault. Unclear why. @@ -1351,57 +1137,34 @@ quint64 Wallet::getBytesSent() const { return m_walletImpl->getBytesSent(); } -bool Wallet::isDeviceConnected() const { - return m_walletImpl->isDeviceConnected(); +QString Wallet::getDaemonLogPath() const { + return QString::fromStdString(m_walletImpl->getDefaultDataDir()) + "/bitmonero.log"; } bool Wallet::setRingDatabase(const QString &path) { return m_walletImpl->setRingDatabase(path.toStdString()); } -void Wallet::onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort) -{ - if (m_walletListener != nullptr) - { - m_walletListener->onPassphraseEntered(passphrase, enter_on_device, entry_abort); - } +void Wallet::setWalletCreationHeight(quint64 height) { + m_walletImpl->setRefreshFromBlockHeight(height); } -Wallet::Wallet(Monero::Wallet *w, QObject *parent) - : QObject(parent) - , m_walletImpl(w) - , m_history(new TransactionHistory(m_walletImpl->history(), this)) - , m_historyModel(nullptr) - , m_addressBook(new AddressBook(m_walletImpl->addressBook(), this)) - , m_addressBookModel(nullptr) - , m_daemonBlockChainHeight(0) - , m_daemonBlockChainTargetHeight(0) - , m_connectionStatus(Wallet::ConnectionStatus_Disconnected) - , m_disconnected(true) - , m_currentSubaddressAccount(0) - , m_subaddress(new Subaddress(m_walletImpl->subaddress(), this)) - , m_subaddressModel(nullptr) - , m_subaddressAccount(new SubaddressAccount(m_walletImpl->subaddressAccount(), this)) - , m_subaddressAccountModel(nullptr) - , m_coinsModel(nullptr) - , m_refreshNow(false) - , m_refreshEnabled(false) - , m_refreshing(false) - , m_scheduler(this) - , m_useSSL(true) - , m_coins(new Coins(m_walletImpl->coins(), this)) -{ - m_walletListener = new WalletListenerImpl(this); - m_walletImpl->setListener(m_walletListener); - m_currentSubaddressAccount = getCacheAttribute(ATTRIBUTE_SUBADDRESS_ACCOUNT).toUInt(); - // start cache timers - m_initialized = false; - m_daemonUsername = ""; - m_daemonPassword = ""; +//! create a view only wallet +bool Wallet::createViewOnly(const QString &path, const QString &password) const { + // Create path + QDir d = QFileInfo(path).absoluteDir(); + d.mkpath(d.absolutePath()); + return m_walletImpl->createWatchOnly(path.toStdString(),password.toStdString(),m_walletImpl->getSeedLanguage()); +} - if (this->status() == Status_Ok) { - startRefreshThread(); - } +bool Wallet::rescanSpent() { + QMutexLocker locker(&m_asyncMutex); + + return m_walletImpl->rescanSpent(); +} + +void Wallet::setNewWallet() { + m_newWallet = true; } Wallet::~Wallet() @@ -1413,57 +1176,15 @@ Wallet::~Wallet() m_scheduler.shutdownWaitForFinished(); - //Monero::WalletManagerFactory::getWalletManager()->closeWallet(m_walletImpl); - if(status() == Status_Critical || status() == Status_BadPassword) + if (status() == Status_Critical || status() == Status_BadPassword) { qDebug("Not storing wallet cache"); - else if( m_walletImpl->store()) - qDebug("Wallet cache stored successfully"); - else - qDebug("Error storing wallet cache"); + } + else { + bool success = m_walletImpl->store(); + success ? qDebug("Wallet cache stored successfully") : qDebug("Error storing wallet cache"); + } + delete m_walletImpl; m_walletImpl = nullptr; - delete m_walletListener; - m_walletListener = NULL; qDebug("m_walletImpl deleted"); } - -void Wallet::startRefreshThread() -{ - const auto future = m_scheduler.run([this] { - constexpr const std::chrono::seconds refreshInterval{10}; - constexpr const std::chrono::milliseconds intervalResolution{100}; - - auto last = std::chrono::steady_clock::now(); - while (!m_scheduler.stopping()) - { - if (m_refreshEnabled && (!isHwBacked() || isDeviceConnected())) - { - const auto now = std::chrono::steady_clock::now(); - const auto elapsed = now - last; - if (elapsed >= refreshInterval || m_refreshNow) - { - m_refreshNow = false; - // Don't call refresh function if we don't have the daemon and target height - // We do this to prevent to UI from getting confused about the amount of blocks that are still remaining - bool haveHeights = refreshHeights(); - if (haveHeights) { - refresh(false); - } - last = std::chrono::steady_clock::now(); - } - } - - std::this_thread::sleep_for(intervalResolution); - } - }); - if (!future.first) - { - throw std::runtime_error("failed to start auto refresh thread"); - } -} - -void Wallet::onRefreshed(bool success) { - if (!success) { - setConnectionStatus(ConnectionStatus_Disconnected); - } -} \ No newline at end of file diff --git a/src/libwalletqt/Wallet.h b/src/libwalletqt/Wallet.h index 84ee30f..b165ab8 100644 --- a/src/libwalletqt/Wallet.h +++ b/src/libwalletqt/Wallet.h @@ -10,9 +10,9 @@ #include #include -#include "wallet/api/wallet2_api.h" // we need to have an access to the Monero::Wallet::Status enum here; +#include "wallet/api/wallet2_api.h" #include "utils/scheduler.h" -#include "PendingTransaction.h" // we need to have an access to the PendingTransaction::Priority enum here; +#include "PendingTransaction.h" #include "UnsignedTransaction.h" #include "utils/networktype.h" #include "PassphraseHelper.h" @@ -86,8 +86,7 @@ class Wallet : public QObject, public PassphrasePrompter Q_OBJECT public: - explicit Wallet(QObject *parent = nullptr); - explicit Wallet(Monero::Wallet *w, QObject * parent = nullptr); + explicit Wallet(Monero::Wallet *w, QObject *parent = nullptr); ~Wallet() override; enum Status { @@ -109,9 +108,64 @@ public: Q_ENUM(ConnectionStatus) + // ##### Status ##### + //! returns last operation's status + Status status() const; + //! return connection status ConnectionStatus connectionStatus() const; + //! returns true if wallet is currently synchronized + bool isSynchronized() const; + + //! return true if wallet is connected to a node + bool isConnected() const; + + //! returns last operation's error message + QString errorString() const; + + //! returns network type of the wallet. + NetworkType::Type nettype() const; + + //! returns if view only wallet + bool viewOnly() const; + + //! return true if deterministic keys + bool isDeterministic() const; + + // ##### Balance ##### + //! returns balance + quint64 balance() const; + quint64 balance(quint32 accountIndex) const; + quint64 balanceAll() const; + + //! returns unlocked balance + quint64 unlockedBalance() const; + quint64 unlockedBalance(quint32 accountIndex) const; + quint64 unlockedBalanceAll() const; + + void updateBalance(); + + // ##### Subaddresses and Accounts ##### + //! returns wallet's public address + QString address(quint32 accountIndex, quint32 addressIndex) const; + + //! returns the subaddress index of the address + SubaddressIndex subaddressIndex(const QString &address) const; + + quint32 currentSubaddressAccount() const; + void switchSubaddressAccount(quint32 accountIndex); + void addSubaddressAccount(const QString& label); + quint32 numSubaddressAccounts() const; + quint32 numSubaddresses(quint32 accountIndex) const; + void addSubaddress(const QString& label); + QString getSubaddressLabel(quint32 accountIndex, quint32 addressIndex) const; + void setSubaddressLabel(quint32 accountIndex, quint32 addressIndex, const QString &label); + void deviceShowAddressAsync(quint32 accountIndex, quint32 addressIndex, const QString &paymentId); + QString getSubaddressLookahead() const; + + // ##### Seed ##### + //! returns mnemonic seed QString getSeed(const QString &seedOffset) const; @@ -123,115 +177,64 @@ public: //! set seed language void setSeedLanguage(const QString &lang); - //! returns last operation's status - Status status() const; + //! Get wallet keys + QString getSecretViewKey() const {return QString::fromStdString(m_walletImpl->secretViewKey());} + QString getPublicViewKey() const {return QString::fromStdString(m_walletImpl->publicViewKey());} + QString getSecretSpendKey() const {return QString::fromStdString(m_walletImpl->secretSpendKey());} + QString getPublicSpendKey() const {return QString::fromStdString(m_walletImpl->publicSpendKey());} - //! returns network type of the wallet. - NetworkType::Type nettype() const; - - //! returns true if wallet was ever synchronized - bool synchronized() const; - - //! returns true if wallet is currently synchronized - bool isSynchronized() const; - - //! return true if wallet is connected to a node - bool isConnected() const; + // ##### Node connection ##### void setOffline(bool offline) const; - //! returns last operation's error message - QString errorString() const; - - //! changes the password using existing parameters (path, seed, seed lang) - bool setPassword(const QString &oldPassword, const QString &newPassword); - - //! verify wallet password - bool verifyPassword(const QString &password); - - //! returns wallet's public address - QString address(quint32 accountIndex, quint32 addressIndex) const; - - //! returns the subaddress index of the address - SubaddressIndex subaddressIndex(const QString &address) const; - - //! returns wallet cache file path - QString cachePath() const; - - //! returns wallet keys file path - QString keysPath() const; - - //! saves wallet to the file by given path - //! empty path stores in current location - void store(); - // void storeAsync(const QJSValue &callback, const QString &path = ""); - - //! initializes wallet asynchronously - void initAsync( - const QString &daemonAddress, - bool trustedDaemon = false, - quint64 upperTransactionLimit = 0, - bool isRecovering = false, - bool isRecoveringFromDevice = false, - quint64 restoreHeight = 0, - const QString &proxyAddress = ""); - - bool setDaemon(const QString &daemonAddress); - - // Set daemon rpc user/pass - void setDaemonLogin(const QString &daemonUsername = "", const QString &daemonPassword = ""); - - //! create a view only wallet - bool createViewOnly(const QString &path, const QString &password) const; - - //! connects to daemon - bool connectToDaemon(); - //! indicates if daemon is trusted void setTrustedDaemon(bool arg); //! indicates if ssl should be used to connect to daemon void setUseSSL(bool ssl); - //! returns balance - quint64 balance() const; - quint64 balance(quint32 accountIndex) const; - quint64 balanceAll() const; + //! Set daemon rpc user/pass + void setDaemonLogin(const QString &daemonUsername = "", const QString &daemonPassword = ""); - //! returns unlocked balance - quint64 unlockedBalance() const; - quint64 unlockedBalance(quint32 accountIndex) const; - quint64 unlockedBalanceAll() const; + //! initializes wallet asynchronously + void initAsync(const QString &daemonAddress, + bool trustedDaemon = false, + quint64 upperTransactionLimit = 0, + const QString &proxyAddress = ""); - //! account/address management - quint32 currentSubaddressAccount() const; - void switchSubaddressAccount(quint32 accountIndex); - void addSubaddressAccount(const QString& label); - quint32 numSubaddressAccounts() const; - quint32 numSubaddresses(quint32 accountIndex) const; - void addSubaddress(const QString& label); - QString getSubaddressLabel(quint32 accountIndex, quint32 addressIndex) const; - void setSubaddressLabel(quint32 accountIndex, quint32 addressIndex, const QString &label); - void deviceShowAddressAsync(quint32 accountIndex, quint32 addressIndex, const QString &paymentId); - QString getSubaddressLookahead() const; + // ##### Synchronization (Refresh) ##### + void startRefresh(); + void pauseRefresh(); - //! hw-device backed wallets + //! returns current wallet's block height + //! (can be less than daemon's blockchain height when wallet sync in progress) + quint64 blockChainHeight() const; + + //! returns daemon's blockchain height + quint64 daemonBlockChainHeight() const; + + //! returns daemon's blockchain target height + quint64 daemonBlockChainTargetHeight() const; + + void syncStatusUpdated(quint64 height, quint64 target); + + void refreshModels(); + + // ##### Hardware wallet ##### bool isHwBacked() const; bool isLedger() const; bool isTrezor() const; + bool isDeviceConnected() const; + //! attempt to reconnect to hw-device bool reconnectDevice(); - //! returns if view only wallet - bool viewOnly() const; - - //! return true if deterministic keys - bool isDeterministic() const; - - //! refresh daemon blockchain and target height - bool refreshHeights(); + // Passphrase entry for hardware wallets + void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort=false); + void onWalletPassphraseNeeded(bool on_device) override; + // ##### Import / Export ##### //! export/import key images bool exportKeyImages(const QString& path, bool all = false); bool importKeyImages(const QString& path); @@ -243,6 +246,35 @@ public: //! import a transaction bool importTransaction(const QString& txid); + // ##### Wallet cache ##### + //! saves wallet to the file by given path + //! empty path stores in current location + void store(); + void storeSafer(); + + //! returns wallet cache file path + QString cachePath() const; + + //! returns wallet keys file path + QString keysPath() const; + + //! changes the password using existing parameters (path, seed, seed lang) + bool setPassword(const QString &oldPassword, const QString &newPassword); + + //! verify wallet password + bool verifyPassword(const QString &password); + + //! Namespace your cacheAttribute keys to avoid collisions + bool cacheAttributeExists(const QString &key); + bool setCacheAttribute(const QString &key, const QString &val); + QString getCacheAttribute(const QString &key) const; + + void addCacheTransaction(const QString &txid, const QString &txHex); + QString getCacheTransaction(const QString &txid) const; + + bool setUserNote(const QString &txid, const QString ¬e); + QString getUserNote(const QString &txid) const; + QString printBlockchain(); QString printTransfers(); QString printPayments(); @@ -261,78 +293,26 @@ public: QString printAddressBook(); QString printScannedPoolTxs(); + // ##### Transactions ##### + void setSelectedInputs(const QStringList &selected); + + void createTransaction(const QString &address, quint64 amount, const QString &description, bool all); + void createTransactionMultiDest(const QVector &addresses, const QVector &amounts, const QString &description); + void sweepOutputs(const QVector &keyImages, QString address, bool churn, int outputs); + + void onCreateTransactionError(const QString &msg); + void commitTransaction(PendingTransaction *tx, const QString &description=""); + + //! deletes transaction and frees memory + void disposeTransaction(PendingTransaction * t); + + //! deletes unsigned transaction and frees memory + void disposeTransaction(UnsignedTransaction * t); + + // ##### Transaction import ##### //! does wallet have txid bool haveTransaction(const QString &txid); - //! refreshes the wallet - bool refresh(bool historyAndSubaddresses = false); - - // pause/resume refresh - void startRefresh(); - void pauseRefresh(); - - //! returns current wallet's block height - //! (can be less than daemon's blockchain height when wallet sync in progress) - quint64 blockChainHeight() const; - - //! returns daemon's blockchain height - quint64 daemonBlockChainHeight() const; - - //! returns daemon's blockchain target height - quint64 daemonBlockChainTargetHeight() const; - - //! creates transaction - PendingTransaction * createTransaction(const QString &dst_addr, const QString &payment_id, - quint64 amount, quint32 mixin_count, - PendingTransaction::Priority priority, - const QStringList &preferredInputs); - - //! creates async transaction - void createTransactionAsync(const QString &dst_addr, const QString &payment_id, - quint64 amount, quint32 mixin_count, - PendingTransaction::Priority priority, const QStringList &preferredInputs); - - //! creates multi-destination transaction - PendingTransaction * createTransactionMultiDest(const QVector &dst_addr, const QVector &amount, - PendingTransaction::Priority priority, const QStringList &preferredInputs); - - //! creates async multi-destination transaction - void createTransactionMultiDestAsync(const QVector &dst_addr, const QVector &amount, - PendingTransaction::Priority priority, const QStringList &preferredInputs); - - - //! creates transaction with all outputs - PendingTransaction * createTransactionAll(const QString &dst_addr, const QString &payment_id, - quint32 mixin_count, PendingTransaction::Priority priority, - const QStringList &preferredInputs); - - //! creates async transaction with all outputs - void createTransactionAllAsync(const QString &dst_addr, const QString &payment_id, - quint32 mixin_count, PendingTransaction::Priority priority, - const QStringList &preferredInputs); - - //! creates transaction with single input - PendingTransaction * createTransactionSingle(const QString &key_image, const QString &dst_addr, - size_t outputs, PendingTransaction::Priority priority); - - //! creates async transaction with single input - void createTransactionSingleAsync(const QString &key_image, const QString &dst_addr, - size_t outputs, PendingTransaction::Priority priority); - - //! creates transaction with selected inputs - PendingTransaction * createTransactionSelected(const QVector &key_images, const QString &dst_addr, - size_t outputs, PendingTransaction::Priority priority); - - //! creates async transaction with selected inputs - void createTransactionSelectedAsync(const QVector &key_images, const QString &dst_addr, - size_t outputs, PendingTransaction::Priority priority); - - //! creates sweep unmixable transaction - PendingTransaction * createSweepUnmixableTransaction(); - - //! creates async sweep unmixable transaction - void createSweepUnmixableTransactionAsync(); - //! Sign a transfer from file UnsignedTransaction * loadTxFile(const QString &fileName); @@ -345,136 +325,73 @@ public: //! Submit a transfer from file bool submitTxFile(const QString &fileName) const; - //! asynchronous transaction commit - void commitTransactionAsync(PendingTransaction * t, const QString &description=""); + // ##### Models ##### + TransactionHistory* history() const; + TransactionHistoryProxyModel* historyModel(); + TransactionHistoryModel* transactionHistoryModel() const; + AddressBook* addressBook() const; + AddressBookModel* addressBookModel() const; + Subaddress* subaddress() const; + SubaddressModel* subaddressModel() const; + SubaddressAccount* subaddressAccount() const; + SubaddressAccountModel* subaddressAccountModel() const; + Coins* coins() const; + CoinsModel* coinsModel() const; - //! deletes transaction and frees memory - void disposeTransaction(PendingTransaction * t); + // ##### Transaction proofs ##### - //! deletes unsigned transaction and frees memory - void disposeTransaction(UnsignedTransaction * t); + QString getTxKey(const QString &txid) const; + void getTxKeyAsync(const QString &txid, const std::function &callback); -// void estimateTransactionFeeAsync(const QString &destination, -// quint64 amount, -// PendingTransaction::Priority priority, -// const QJSValue &callback); - - //! returns transaction history - TransactionHistory * history() const; - - //! returns transaction history model - TransactionHistoryProxyModel *historyModel() const; - - //! returns transaction history model (the real one) - TransactionHistoryModel *transactionHistoryModel() const; - - //! returns Address book - AddressBook *addressBook() const; - - //! returns address book model - AddressBookModel *addressBookModel() const; - - //! returns subaddress - Subaddress *subaddress(); - - //! returns subaddress model - SubaddressModel *subaddressModel(); - - //! returns subaddress account - SubaddressAccount *subaddressAccount() const; - - //! returns subaddress account model - SubaddressAccountModel *subaddressAccountModel() const; - - //! returns coins - Coins *coins() const; - - //! return coins model - CoinsModel *coinsModel() const; - - //! generate payment id - QString generatePaymentId() const; - - //! integrated address - QString integratedAddress(const QString &paymentId) const; + TxKeyResult checkTxKey(const QString &txid, const QString &tx_key, const QString &address); + TxProof getTxProof(const QString &txid, const QString &address, const QString &message) const; + TxProofResult checkTxProof(const QString &txid, const QString &address, const QString &message, const QString &signature); + void checkTxProofAsync(const QString &txid, const QString &address, const QString &message, const QString &signature); + TxProof getSpendProof(const QString &txid, const QString &message) const; + QPair checkSpendProof(const QString &txid, const QString &message, const QString &signature) const; + void checkSpendProofAsync(const QString &txid, const QString &message, const QString &signature); + // ##### Sign / Verify message ##### //! signing a message QString signMessage(const QString &message, bool filename = false, const QString &address = "") const; //! verify a signed message bool verifySignedMessage(const QString &message, const QString &address, const QString &signature, bool filename = false) const; - //! Parse URI + // ##### URI Parsing ##### bool parse_uri(const QString &uri, QString &address, QString &payment_id, uint64_t &amount, QString &tx_description, QString &recipient_name, QVector &unknown_parameters, QString &error) const; QVariantMap parse_uri_to_object(const QString &uri) const; QString make_uri(const QString &address, quint64 &amount, const QString &description, const QString &recipient) const; - //! Namespace your cacheAttribute keys to avoid collisions - bool cacheAttributeExists(const QString &key); - bool setCacheAttribute(const QString &key, const QString &val); - QString getCacheAttribute(const QString &key) const; - - bool setUserNote(const QString &txid, const QString ¬e); - QString getUserNote(const QString &txid) const; - QString getTxKey(const QString &txid) const; - void getTxKeyAsync(const QString &txid, const std::function &callback); - - TxKeyResult checkTxKey(const QString &txid, const QString &tx_key, const QString &address); - TxProof getTxProof(const QString &txid, const QString &address, const QString &message) const; - // void getTxProofAsync(const QString &txid, const QString &address, const QString &message, const QJSValue &callback); - //QString checkTxProof(const QString &txid, const QString &address, const QString &message, const QString &signature); - TxProofResult checkTxProof(const QString &txid, const QString &address, const QString &message, const QString &signature); - void checkTxProofAsync(const QString &txid, const QString &address, const QString &message, const QString &signature); - TxProof getSpendProof(const QString &txid, const QString &message) const; - // void getSpendProofAsync(const QString &txid, const QString &message, const QJSValue &callback); - QPair checkSpendProof(const QString &txid, const QString &message, const QString &signature) const; - void checkSpendProofAsync(const QString &txid, const QString &message, const QString &signature); - // Rescan spent outputs - bool rescanSpent(); - - // check if fork rules should be used - bool useForkRules(quint8 version, quint64 earlyBlocks = 0) const; - - //! Get wallet keys - QString getSecretViewKey() const {return QString::fromStdString(m_walletImpl->secretViewKey());} - QString getPublicViewKey() const {return QString::fromStdString(m_walletImpl->publicViewKey());} - QString getSecretSpendKey() const {return QString::fromStdString(m_walletImpl->secretSpendKey());} - QString getPublicSpendKey() const {return QString::fromStdString(m_walletImpl->publicSpendKey());} - - quint64 getWalletCreationHeight() const {return m_walletImpl->getRefreshFromBlockHeight();} - void setWalletCreationHeight(quint64 height); - - QString getDaemonLogPath() const; - - // Blackballed outputs - bool blackballOutput(const QString &amount, const QString &offset); - bool blackballOutputs(const QList &outputs, bool add); - bool blackballOutputs(const QString &filename, bool add); - bool unblackballOutput(const QString &amount, const QString &offset); - - // Rings - QString getRing(const QString &key_image); - QString getRings(const QString &txid); - bool setRing(const QString &key_image, const QString &ring, bool relative); - - // key reuse mitigation options - void segregatePreForkOutputs(bool segregate); - void segregationHeight(quint64 height); - void keyReuseMitigation2(bool mitigation); - - // Passphrase entry for hardware wallets - void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort=false); - void onWalletPassphraseNeeded(bool on_device) override; + // ##### Misc / Unused ##### quint64 getBytesReceived() const; quint64 getBytesSent() const; - bool isDeviceConnected() const; + QString getDaemonLogPath() const; bool setRingDatabase(const QString &path); - // TODO: setListener() when it implemented in API + quint64 getWalletCreationHeight() const {return m_walletImpl->getRefreshFromBlockHeight();} + void setWalletCreationHeight(quint64 height); + + //! Rescan spent outputs + bool rescanSpent(); + + //! Indicates that the wallet is new + void setNewWallet(); + + //! create a view only wallet + bool createViewOnly(const QString &path, const QString &password) const; + + PendingTransaction::Priority tx_priority = PendingTransaction::Priority::Priority_Low; + + QString tmpTxDescription; // TODO: remove the need for this var + bool refreshedOnce = false; + + void onHeightsRefreshed(bool success, quint64 daemonHeight, quint64 targetHeight); + signals: // emitted on every event happened with wallet // (money sent/received, new block) @@ -488,15 +405,12 @@ signals: void moneyReceived(const QString &txId, quint64 amount); void unconfirmedMoneyReceived(const QString &txId, quint64 amount); void newBlock(quint64 height, quint64 targetHeight); - void addressBookChanged() const; - void historyModelChanged() const; void walletCreationHeightChanged(); void deviceButtonRequest(quint64 buttonCode); void deviceButtonPressed(); void deviceError(const QString &message); void walletPassphraseNeeded(bool onDevice); void transactionCommitted(bool status, PendingTransaction *t, const QStringList& txid); - void heightRefreshed(quint64 walletHeight, quint64 daemonHeight, quint64 targetHeight) const; void deviceShowAddressShowed(); void transactionProofVerified(TxProofResult result); void spendProofVerified(QPair result); @@ -506,72 +420,82 @@ signals: void connectionStatusChanged(int status) const; void currentSubaddressAccountChanged() const; - void disconnectedChanged() const; - void proxyAddressChanged() const; - void refreshingChanged() const; + + void refreshSync(int height, int target); + void blockchainSync(int height, int target); + void synchronized(); + void balanceUpdated(quint64 balance, quint64 spendable); + void keysCorrupted(); + + void endTransaction(); + void createTransactionSuccess(PendingTransaction *tx, const QVector &address); + void donationSent(); + void walletRefreshed(); + + void initiateTransaction(); + void createTransactionError(QString message); + + void selectedInputsChanged(const QStringList &selectedInputs); + + void multiBroadcast(PendingTransaction *tx); + void heightsRefreshed(bool success, quint64 daemonHeight, quint64 targetHeight); private: - //! initializes wallet - 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 onRefreshed(bool success); - + // ###### Status ###### void setConnectionStatus(ConnectionStatus value); - QString getProxyAddress() const; - void setProxyAddress(QString address); - void startRefreshThread(); + // ##### Synchronization (Refresh) ##### + void startRefreshThread(); void onNewBlock(uint64_t height); + void onUpdated(); + void onRefreshed(bool success, const QString &message); + + // ##### Transactions ##### + void onTransactionCreated(PendingTransaction *tx, const QVector &address); private: friend class WalletManager; friend class WalletListenerImpl; - //! libwallet's - Monero::Wallet * m_walletImpl; - // history lifetime managed by wallet; - TransactionHistory * m_history; - // Used for UI history view - mutable TransactionHistoryModel * m_historyModel; - mutable TransactionHistoryProxyModel * m_historySortFilterModel; - QString m_paymentId; - AddressBook * m_addressBook; - mutable AddressBookModel * m_addressBookModel; - mutable quint64 m_daemonBlockChainHeight; - mutable quint64 m_daemonBlockChainTargetHeight; - mutable ConnectionStatus m_connectionStatus; + Monero::Wallet *m_walletImpl; + + TransactionHistory *m_history; + TransactionHistoryModel *m_historyModel; + TransactionHistoryProxyModel *m_historySortFilterModel; + + AddressBook *m_addressBook; + AddressBookModel *m_addressBookModel; + + quint64 m_daemonBlockChainHeight; + quint64 m_daemonBlockChainTargetHeight; + + ConnectionStatus m_connectionStatus; - bool m_disconnected; - mutable bool m_initialized; uint32_t m_currentSubaddressAccount; - Subaddress * m_subaddress; - mutable SubaddressModel * m_subaddressModel; - SubaddressAccount * m_subaddressAccount; - mutable SubaddressAccountModel * m_subaddressAccountModel; - Coins * m_coins; - mutable CoinsModel * m_coinsModel; + Subaddress *m_subaddress; + SubaddressModel *m_subaddressModel; + SubaddressAccount *m_subaddressAccount; + SubaddressAccountModel *m_subaddressAccountModel; + + Coins *m_coins; + CoinsModel *m_coinsModel; + QMutex m_asyncMutex; QString m_daemonUsername; QString m_daemonPassword; - QString m_proxyAddress; - mutable QMutex m_proxyMutex; + + QMutex m_proxyMutex; std::atomic m_refreshNow; std::atomic m_refreshEnabled; - std::atomic m_refreshing; WalletListenerImpl *m_walletListener; FutureScheduler m_scheduler; - int m_connectionTimeout = 30; + bool m_useSSL; + bool donationSending = false; + bool m_newWallet = false; + + QTimer *m_storeTimer = nullptr; + std::set m_selectedInputs; }; diff --git a/src/libwalletqt/WalletListenerImpl.cpp b/src/libwalletqt/WalletListenerImpl.cpp index c1ec41f..e5a47ed 100644 --- a/src/libwalletqt/WalletListenerImpl.cpp +++ b/src/libwalletqt/WalletListenerImpl.cpp @@ -3,6 +3,7 @@ #include "WalletListenerImpl.h" #include "Wallet.h" +#include "WalletManager.h" WalletListenerImpl::WalletListenerImpl(Wallet * w) : m_wallet(w) @@ -11,28 +12,40 @@ WalletListenerImpl::WalletListenerImpl(Wallet * w) } +// Beware! +// Do not call non-signal m_wallet functions here +// Nothing here runs in the GUI thread + void WalletListenerImpl::moneySpent(const std::string &txId, uint64_t amount) { - qDebug() << __FUNCTION__; - emit m_wallet->moneySpent(QString::fromStdString(txId), amount); + // Outgoing tx included in a block + QString qTxId = QString::fromStdString(txId); + qDebug() << Q_FUNC_INFO << qTxId << " " << WalletManager::displayAmount(amount); + + emit m_wallet->moneySpent(qTxId, amount); } void WalletListenerImpl::moneyReceived(const std::string &txId, uint64_t amount) { - qDebug() << __FUNCTION__; - emit m_wallet->moneyReceived(QString::fromStdString(txId), amount); + // Incoming tx included in a block. + QString qTxId = QString::fromStdString(txId); + qDebug() << Q_FUNC_INFO << qTxId << " " << WalletManager::displayAmount(amount); + + emit m_wallet->moneyReceived(qTxId, amount); } void WalletListenerImpl::unconfirmedMoneyReceived(const std::string &txId, uint64_t amount) { - qDebug() << __FUNCTION__; - emit m_wallet->unconfirmedMoneyReceived(QString::fromStdString(txId), amount); + // Incoming tx in pool + QString qTxId = QString::fromStdString(txId); + qDebug() << Q_FUNC_INFO << qTxId << " " << WalletManager::displayAmount(amount); + + emit m_wallet->unconfirmedMoneyReceived(qTxId, amount); } void WalletListenerImpl::newBlock(uint64_t height) { - // qDebug() << __FUNCTION__; - m_wallet->onNewBlock(height); + // Called whenever a new block gets scanned by the wallet emit m_wallet->newBlock(height, m_wallet->daemonBlockChainTargetHeight()); } @@ -45,7 +58,6 @@ void WalletListenerImpl::updated() void WalletListenerImpl::refreshed(bool success) { QString message = m_wallet->errorString(); - m_wallet->onRefreshed(success); emit m_wallet->refreshed(success, message); } diff --git a/src/main.cpp b/src/main.cpp index 1e52c87..15894be 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -232,7 +232,7 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) { EventFilter filter; app.installEventFilter(&filter); - WindowManager windowManager(&filter); + WindowManager windowManager(QCoreApplication::instance(), &filter); QObject::connect(&app, &SingleApplication::instanceStarted, [&windowManager]() { windowManager.raise(); diff --git a/src/utils/TorManager.cpp b/src/utils/TorManager.cpp index 64177d0..fe64009 100644 --- a/src/utils/TorManager.cpp +++ b/src/utils/TorManager.cpp @@ -7,9 +7,10 @@ #include #include +#include "utils/config.h" #include "utils/Utils.h" #include "utils/os/tails.h" -#include "appcontext.h" +#include "utils/os/whonix.h" #include "config-feather.h" TorManager::TorManager(QObject *parent) @@ -313,4 +314,8 @@ TorManager* TorManager::instance() } return m_instance; -} \ No newline at end of file +} + +TorManager::~TorManager() { + qDebug() << "~TorManager"; +} diff --git a/src/utils/TorManager.h b/src/utils/TorManager.h index 31c30be..c0d4e30 100644 --- a/src/utils/TorManager.h +++ b/src/utils/TorManager.h @@ -18,6 +18,7 @@ Q_OBJECT public: explicit TorManager(QObject *parent = nullptr); + ~TorManager() override; void init(); void start(); diff --git a/src/utils/nodes.cpp b/src/utils/nodes.cpp index 6ff97cd..3f9d7af 100644 --- a/src/utils/nodes.cpp +++ b/src/utils/nodes.cpp @@ -4,9 +4,10 @@ #include #include "nodes.h" +#include "utils/AppData.h" #include "utils/Utils.h" #include "utils/os/tails.h" -#include "appcontext.h" +#include "utils/os/whonix.h" #include "constants.h" #include "utils/WebsocketNotifier.h" #include "utils/TorManager.h" @@ -94,21 +95,21 @@ void NodeList::ensureStructure(QJsonObject &obj, NetworkType::Type networkType) obj[networkTypeStr] = netTypeObj; } -Nodes::Nodes(QObject *parent) +Nodes::Nodes(QObject *parent, Wallet *wallet) : QObject(parent) , modelWebsocket(new NodeModel(NodeSource::websocket, this)) , modelCustom(new NodeModel(NodeSource::custom, this)) , m_connection(FeatherNode()) + , m_wallet(wallet) { // TODO: This class is in desperate need of refactoring this->loadConfig(); connect(websocketNotifier(), &WebsocketNotifier::NodesReceived, this, &Nodes::onWSNodesReceived); -} -void Nodes::setContext(AppContext *ctx) { - m_ctx = ctx; - connect(m_ctx, &AppContext::walletRefreshed, this, &Nodes::onWalletRefreshed); + if (m_wallet) { + connect(m_wallet, &Wallet::walletRefreshed, this, &Nodes::onWalletRefreshed); + } } void Nodes::loadConfig() { @@ -195,7 +196,11 @@ void Nodes::connectToNode() { } void Nodes::connectToNode(const FeatherNode &node) { - if (!m_ctx) { + if (!m_wallet) { + return; + } + + if (!m_allowConnection) { return; } @@ -217,11 +222,11 @@ void Nodes::connectToNode(const FeatherNode &node) { qInfo() << QString("Attempting to connect to %1 (%2)").arg(node.toAddress(), node.custom ? "custom" : "ws"); if (!node.url.userName().isEmpty() && !node.url.password().isEmpty()) { - m_ctx->wallet->setDaemonLogin(node.url.userName(), node.url.password()); + m_wallet->setDaemonLogin(node.url.userName(), node.url.password()); } // Don't use SSL over Tor/i2p - m_ctx->wallet->setUseSSL(!node.isAnonymityNetwork()); + m_wallet->setUseSSL(!node.isAnonymityNetwork()); QString proxyAddress; if (useSocks5Proxy(node)) { @@ -233,7 +238,7 @@ void Nodes::connectToNode(const FeatherNode &node) { } } - m_ctx->wallet->initAsync(node.toAddress(), true, 0, false, false, 0, proxyAddress); + m_wallet->initAsync(node.toAddress(), true, 0, proxyAddress); m_connection = node; m_connection.isActive = false; @@ -244,16 +249,16 @@ void Nodes::connectToNode(const FeatherNode &node) { } void Nodes::autoConnect(bool forceReconnect) { - if (!m_ctx) { + if (!m_wallet) { return; } // this function is responsible for automatically connecting to a daemon. - if (m_ctx->wallet == nullptr || !m_enableAutoconnect) { + if (m_wallet == nullptr || !m_enableAutoconnect) { return; } - Wallet::ConnectionStatus status = m_ctx->wallet->connectionStatus(); + Wallet::ConnectionStatus status = m_wallet->connectionStatus(); bool wsMode = (this->source() == NodeSource::websocket); if (wsMode && !m_wsNodesReceived && websocketNodes().count() == 0) { @@ -423,7 +428,7 @@ bool Nodes::useOnionNodes() { } if (privacyLevel == Config::allTorExceptInitSync) { - if (m_ctx && m_ctx->refreshed) { + if (m_wallet && m_wallet->refreshedOnce) { return true; } @@ -431,7 +436,7 @@ bool Nodes::useOnionNodes() { int initSyncThreshold = config()->get(Config::initSyncThreshold).toInt(); int networkHeight = appData()->heights[constants::networkType]; - if (m_ctx && m_ctx->wallet->blockChainHeight() > (networkHeight - initSyncThreshold)) { + if (m_wallet && m_wallet->blockChainHeight() > (networkHeight - initSyncThreshold)) { return true; } } @@ -584,4 +589,12 @@ int Nodes::modeHeight(const QList &nodes) { } return mode_height; -} \ No newline at end of file +} + +void Nodes::allowConnection() { + m_allowConnection = true; +} + +Nodes::~Nodes() { + qDebug() << "~Nodes"; +} diff --git a/src/utils/nodes.h b/src/utils/nodes.h index ed26123..5228dfd 100644 --- a/src/utils/nodes.h +++ b/src/utils/nodes.h @@ -118,9 +118,11 @@ class Nodes : public QObject { Q_OBJECT public: - explicit Nodes(QObject *parent = nullptr); - void setContext(AppContext *ctx); + explicit Nodes(QObject *parent, Wallet *wallet); + ~Nodes() override; + void loadConfig(); + void allowConnection(); NodeSource source(); FeatherNode connection(); @@ -144,7 +146,7 @@ private slots: void onWalletRefreshed(); private: - AppContext *m_ctx = nullptr; + Wallet *m_wallet = nullptr; QJsonObject m_configJson; NodeList m_nodes; @@ -159,6 +161,8 @@ private: bool m_wsNodesReceived = false; bool m_enableAutoconnect = true; + bool m_allowConnection = false; + FeatherNode pickEligibleNode(); bool useOnionNodes(); diff --git a/src/utils/xmrig.cpp b/src/utils/xmrig.cpp index b2e354c..418fd57 100644 --- a/src/utils/xmrig.cpp +++ b/src/utils/xmrig.cpp @@ -4,10 +4,10 @@ #include #include +#include "utils/config.h" #include "utils/Utils.h" #include "utils/xmrig.h" #include "utils/TorManager.h" -#include "appcontext.h" XmRig::XmRig(const QString &configDir, QObject *parent) : QObject(parent) diff --git a/src/widgets/CCSWidget.cpp b/src/widgets/CCSWidget.cpp index da27e54..96f3842 100644 --- a/src/widgets/CCSWidget.cpp +++ b/src/widgets/CCSWidget.cpp @@ -9,6 +9,7 @@ #include #include "CCSProgressDelegate.h" +#include "utils/Utils.h" CCSWidget::CCSWidget(QWidget *parent) : QWidget(parent) @@ -37,8 +38,9 @@ void CCSWidget::linkClicked() { QModelIndex index = ui->tableView->currentIndex(); auto entry = m_model->entry(index.row()); - if (entry) + if (entry) { Utils::externalLinkWarning(this, entry->url); + } } void CCSWidget::donateClicked() { diff --git a/src/widgets/CCSWidget.h b/src/widgets/CCSWidget.h index 3590d4f..0b47e4d 100644 --- a/src/widgets/CCSWidget.h +++ b/src/widgets/CCSWidget.h @@ -10,7 +10,6 @@ #include #include -#include "appcontext.h" #include "model/CCSModel.h" #include "widgets/CCSEntry.h" diff --git a/src/widgets/LocalMoneroWidget.cpp b/src/widgets/LocalMoneroWidget.cpp index 6865b13..e00a900 100644 --- a/src/widgets/LocalMoneroWidget.cpp +++ b/src/widgets/LocalMoneroWidget.cpp @@ -13,10 +13,10 @@ #include "utils/NetworkManager.h" #include "utils/WebsocketNotifier.h" -LocalMoneroWidget::LocalMoneroWidget(QWidget *parent, QSharedPointer ctx) +LocalMoneroWidget::LocalMoneroWidget(QWidget *parent, Wallet *wallet) : QWidget(parent) , ui(new Ui::LocalMoneroWidget) - , m_ctx(std::move(ctx)) + , m_wallet(wallet) { ui->setupUi(this); diff --git a/src/widgets/LocalMoneroWidget.h b/src/widgets/LocalMoneroWidget.h index c62409e..ffb470a 100644 --- a/src/widgets/LocalMoneroWidget.h +++ b/src/widgets/LocalMoneroWidget.h @@ -7,8 +7,8 @@ #include #include "api/LocalMoneroApi.h" -#include "appcontext.h" #include "model/LocalMoneroModel.h" +#include "libwalletqt/Wallet.h" namespace Ui { class LocalMoneroWidget; @@ -19,7 +19,7 @@ class LocalMoneroWidget : public QWidget Q_OBJECT public: - explicit LocalMoneroWidget(QWidget *parent, QSharedPointer ctx); + explicit LocalMoneroWidget(QWidget *parent, Wallet *wallet); ~LocalMoneroWidget() override; public slots: @@ -42,7 +42,7 @@ private: void updatePaymentMethods(); QScopedPointer ui; - QSharedPointer m_ctx; + Wallet *m_wallet; int m_currentPage = 0; diff --git a/src/widgets/NodeWidget.h b/src/widgets/NodeWidget.h index 1ef0299..d102ba8 100644 --- a/src/widgets/NodeWidget.h +++ b/src/widgets/NodeWidget.h @@ -8,7 +8,6 @@ #include #include -#include "appcontext.h" #include "model/NodeModel.h" #include "utils/nodes.h" diff --git a/src/widgets/TickerWidget.cpp b/src/widgets/TickerWidget.cpp index 0a17e7b..b4a8ef2 100644 --- a/src/widgets/TickerWidget.cpp +++ b/src/widgets/TickerWidget.cpp @@ -6,11 +6,12 @@ #include "constants.h" #include "utils/AppData.h" +#include "utils/config.h" -TickerWidgetBase::TickerWidgetBase(QWidget *parent, QSharedPointer ctx) +TickerWidgetBase::TickerWidgetBase(QWidget *parent, Wallet *wallet) : QWidget(parent) , ui(new Ui::TickerWidget) - , m_ctx(std::move(ctx)) + , m_wallet(wallet) { ui->setupUi(this); @@ -53,8 +54,8 @@ void TickerWidgetBase::setDisplayText(const QString &text) { } // BalanceTickerWidget -BalanceTickerWidget::BalanceTickerWidget(QWidget *parent, QSharedPointer ctx, bool totalBalance) - : TickerWidgetBase(parent, std::move(ctx)) +BalanceTickerWidget::BalanceTickerWidget(QWidget *parent, Wallet *wallet, bool totalBalance) + : TickerWidgetBase(parent, wallet) , m_totalBalance(totalBalance) { if (totalBalance) @@ -64,13 +65,13 @@ BalanceTickerWidget::BalanceTickerWidget(QWidget *parent, QSharedPointersetPercentageVisible(false); - connect(m_ctx.get(), &AppContext::balanceUpdated, this, &BalanceTickerWidget::updateDisplay); + connect(m_wallet, &Wallet::balanceUpdated, this, &BalanceTickerWidget::updateDisplay); connect(&appData()->prices, &Prices::fiatPricesUpdated, this, &BalanceTickerWidget::updateDisplay); connect(&appData()->prices, &Prices::fiatPricesUpdated, this, &BalanceTickerWidget::updateDisplay); } void BalanceTickerWidget::updateDisplay() { - double balance = (m_totalBalance ? m_ctx->wallet->balanceAll() : m_ctx->wallet->balance()) / constants::cdiv; + double balance = (m_totalBalance ? m_wallet->balanceAll() : m_wallet->balance()) / constants::cdiv; QString fiatCurrency = config()->get(Config::preferredFiatCurrency).toString(); double balanceFiatAmount = appData()->prices.convert("XMR", fiatCurrency, balance); if (balanceFiatAmount < 0) @@ -79,8 +80,8 @@ void BalanceTickerWidget::updateDisplay() { } // PriceTickerWidget -PriceTickerWidget::PriceTickerWidget(QWidget *parent, QSharedPointer ctx, QString symbol) - : TickerWidgetBase(parent, std::move(ctx)) +PriceTickerWidget::PriceTickerWidget(QWidget *parent, Wallet *wallet, QString symbol) + : TickerWidgetBase(parent, wallet) , m_symbol(std::move(symbol)) { this->setTitle(m_symbol); @@ -107,8 +108,8 @@ void PriceTickerWidget::updateDisplay() { } //RatioTickerWidget -RatioTickerWidget::RatioTickerWidget(QWidget *parent, QSharedPointer ctx, QString symbol1, QString symbol2) - : TickerWidgetBase(parent, std::move(ctx)) +RatioTickerWidget::RatioTickerWidget(QWidget *parent, Wallet *wallet, QString symbol1, QString symbol2) + : TickerWidgetBase(parent, wallet) , m_symbol1(std::move(symbol1)) , m_symbol2(std::move(symbol2)) { diff --git a/src/widgets/TickerWidget.h b/src/widgets/TickerWidget.h index 7684586..96c8085 100644 --- a/src/widgets/TickerWidget.h +++ b/src/widgets/TickerWidget.h @@ -6,7 +6,7 @@ #include -#include "appcontext.h" +#include "libwalletqt/Wallet.h" namespace Ui { class TickerWidget; @@ -17,7 +17,7 @@ class TickerWidgetBase : public QWidget Q_OBJECT public: - explicit TickerWidgetBase(QWidget *parent, QSharedPointer ctx); + explicit TickerWidgetBase(QWidget *parent, Wallet *wallet); ~TickerWidgetBase() override; void setTitle(const QString &title); @@ -34,7 +34,7 @@ private: QScopedPointer ui; protected: - QSharedPointer m_ctx; + Wallet *m_wallet; }; class BalanceTickerWidget : public TickerWidgetBase @@ -42,7 +42,7 @@ class BalanceTickerWidget : public TickerWidgetBase Q_OBJECT public: - explicit BalanceTickerWidget(QWidget *parent, QSharedPointer ctx, bool totalBalance); + explicit BalanceTickerWidget(QWidget *parent, Wallet *wallet, bool totalBalance); public slots: void updateDisplay() override; @@ -56,7 +56,7 @@ class PriceTickerWidget : public TickerWidgetBase Q_OBJECT public: - explicit PriceTickerWidget(QWidget *parent, QSharedPointer ctx, QString symbol); + explicit PriceTickerWidget(QWidget *parent, Wallet *wallet, QString symbol); public slots: void updateDisplay() override; @@ -70,7 +70,7 @@ class RatioTickerWidget : public TickerWidgetBase Q_OBJECT public: - explicit RatioTickerWidget(QWidget *parent, QSharedPointer ctx, QString symbol1, QString symbol2); + explicit RatioTickerWidget(QWidget *parent, Wallet *wallet, QString symbol1, QString symbol2); public slots: void updateDisplay() override; diff --git a/src/widgets/XMRigWidget.cpp b/src/widgets/XMRigWidget.cpp index 444b685..f3c676f 100644 --- a/src/widgets/XMRigWidget.cpp +++ b/src/widgets/XMRigWidget.cpp @@ -13,11 +13,12 @@ #include #include "utils/Icons.h" +#include "utils/Utils.h" -XMRigWidget::XMRigWidget(QSharedPointer ctx, QWidget *parent) +XMRigWidget::XMRigWidget(Wallet *wallet, QWidget *parent) : QWidget(parent) , ui(new Ui::XMRigWidget) - , m_ctx(std::move(ctx)) + , m_wallet(wallet) , m_XMRig(new XmRig(Config::defaultConfigDir().path())) , m_model(new QStandardItemModel(this)) , m_contextMenu(new QMenu(this)) @@ -96,25 +97,25 @@ XMRigWidget::XMRigWidget(QSharedPointer ctx, QWidget *parent) ui->relayTor->setChecked(config()->get(Config::xmrigNetworkTor).toBool()); // Receiving address - auto username = m_ctx->wallet->getCacheAttribute("feather.xmrig_username"); + auto username = m_wallet->getCacheAttribute("feather.xmrig_username"); if (!username.isEmpty()) { ui->lineEdit_address->setText(username); } connect(ui->lineEdit_address, &QLineEdit::textChanged, [=]() { - m_ctx->wallet->setCacheAttribute("feather.xmrig_username", ui->lineEdit_address->text()); + m_wallet->setCacheAttribute("feather.xmrig_username", ui->lineEdit_address->text()); }); connect(ui->btn_fillPrimaryAddress, &QPushButton::clicked, this, &XMRigWidget::onUsePrimaryAddressClicked); // Password - auto password = m_ctx->wallet->getCacheAttribute("feather.xmrig_password"); + auto password = m_wallet->getCacheAttribute("feather.xmrig_password"); if (!password.isEmpty()) { ui->lineEdit_password->setText(password); } else { ui->lineEdit_password->setText("featherwallet"); - m_ctx->wallet->setCacheAttribute("feather.xmrig_password", ui->lineEdit_password->text()); + m_wallet->setCacheAttribute("feather.xmrig_password", ui->lineEdit_password->text()); } connect(ui->lineEdit_password, &QLineEdit::textChanged, [=]() { - m_ctx->wallet->setCacheAttribute("feather.xmrig_password", ui->lineEdit_password->text()); + m_wallet->setCacheAttribute("feather.xmrig_password", ui->lineEdit_password->text()); }); // [Status] tab @@ -171,7 +172,7 @@ void XMRigWidget::onClearClicked() { } void XMRigWidget::onUsePrimaryAddressClicked() { - ui->lineEdit_address->setText(m_ctx->wallet->address(0, 0)); + ui->lineEdit_address->setText(m_wallet->address(0, 0)); } void XMRigWidget::onStartClicked() { @@ -193,8 +194,8 @@ void XMRigWidget::onStartClicked() { } // username is receiving address usually - auto username = m_ctx->wallet->getCacheAttribute("feather.xmrig_username"); - auto password = m_ctx->wallet->getCacheAttribute("feather.xmrig_password"); + auto username = m_wallet->getCacheAttribute("feather.xmrig_username"); + auto password = m_wallet->getCacheAttribute("feather.xmrig_password"); if (username.isEmpty()) { ui->console->appendPlainText("Please specify a receiving address on the Settings screen."); @@ -203,7 +204,7 @@ void XMRigWidget::onStartClicked() { if (address.contains("cryptonote.social") && !username.contains(".")) { // cryptonote social requires ., we'll just grab a few chars from primary addy - username = QString("%1.%2").arg(username, m_ctx->wallet->address(0, 0).mid(0, 6)); + username = QString("%1.%2").arg(username, m_wallet->address(0, 0).mid(0, 6)); } int threads = ui->threadSlider->value(); diff --git a/src/widgets/XMRigWidget.h b/src/widgets/XMRigWidget.h index b7eb8e8..a7e3f67 100644 --- a/src/widgets/XMRigWidget.h +++ b/src/widgets/XMRigWidget.h @@ -7,10 +7,11 @@ #include #include #include +#include -#include "appcontext.h" #include "utils/xmrig.h" #include "utils/config.h" +#include "libwalletqt/Wallet.h" namespace Ui { class XMRigWidget; @@ -21,7 +22,7 @@ class XMRigWidget : public QWidget Q_OBJECT public: - explicit XMRigWidget(QSharedPointer ctx, QWidget *parent = nullptr); + explicit XMRigWidget(Wallet *wallet, QWidget *parent = nullptr); ~XMRigWidget() override; QStandardItemModel *model(); @@ -63,7 +64,7 @@ private: bool checkXMRigPath(); QScopedPointer ui; - QSharedPointer m_ctx; + Wallet *m_wallet; XmRig *m_XMRig; QStandardItemModel *m_model; QMenu *m_contextMenu; diff --git a/src/wizard/PageHardwareDevice.h b/src/wizard/PageHardwareDevice.h index 0e0ba65..ab47ded 100644 --- a/src/wizard/PageHardwareDevice.h +++ b/src/wizard/PageHardwareDevice.h @@ -9,7 +9,6 @@ #include #include -#include "appcontext.h" #include "WalletWizard.h" namespace Ui { diff --git a/src/wizard/PageMenu.h b/src/wizard/PageMenu.h index 6710dd4..99482d7 100644 --- a/src/wizard/PageMenu.h +++ b/src/wizard/PageMenu.h @@ -8,8 +8,6 @@ #include #include -#include "appcontext.h" - namespace Ui { class PageMenu; } diff --git a/src/wizard/PageNetwork.h b/src/wizard/PageNetwork.h index f07306c..a9df6fc 100644 --- a/src/wizard/PageNetwork.h +++ b/src/wizard/PageNetwork.h @@ -8,7 +8,6 @@ #include #include -#include "appcontext.h" #include "utils/nodes.h" namespace Ui { diff --git a/src/wizard/PageNetworkProxy.h b/src/wizard/PageNetworkProxy.h index ba6ba62..e666acc 100644 --- a/src/wizard/PageNetworkProxy.h +++ b/src/wizard/PageNetworkProxy.h @@ -6,8 +6,6 @@ #include -#include "appcontext.h" - namespace Ui { class PageNetworkProxy; } diff --git a/src/wizard/PageNetworkWebsocket.h b/src/wizard/PageNetworkWebsocket.h index 443e161..dd0b0ac 100644 --- a/src/wizard/PageNetworkWebsocket.h +++ b/src/wizard/PageNetworkWebsocket.h @@ -6,8 +6,6 @@ #include -#include "appcontext.h" - namespace Ui { class PageNetworkWebsocket; } diff --git a/src/wizard/PageOpenWallet.h b/src/wizard/PageOpenWallet.h index a39ec55..3f4cd55 100644 --- a/src/wizard/PageOpenWallet.h +++ b/src/wizard/PageOpenWallet.h @@ -5,10 +5,10 @@ #define FEATHER_OPENWALLET_H #include +#include #include #include -#include "appcontext.h" #include "model/WalletKeysFilesModel.h" namespace Ui { diff --git a/src/wizard/PageSetPassword.h b/src/wizard/PageSetPassword.h index 12f2d06..32d1be1 100644 --- a/src/wizard/PageSetPassword.h +++ b/src/wizard/PageSetPassword.h @@ -7,7 +7,6 @@ #include #include -#include "appcontext.h" #include "WalletWizard.h" namespace Ui { diff --git a/src/wizard/PageSetRestoreHeight.h b/src/wizard/PageSetRestoreHeight.h index e7f0ff1..d3f77f3 100644 --- a/src/wizard/PageSetRestoreHeight.h +++ b/src/wizard/PageSetRestoreHeight.h @@ -7,7 +7,6 @@ #include #include -#include "appcontext.h" #include "WalletWizard.h" namespace Ui { diff --git a/src/wizard/PageSetSeedPassphrase.h b/src/wizard/PageSetSeedPassphrase.h index bed1b92..fd263fb 100644 --- a/src/wizard/PageSetSeedPassphrase.h +++ b/src/wizard/PageSetSeedPassphrase.h @@ -7,7 +7,6 @@ #include #include -#include "appcontext.h" #include "WalletWizard.h" namespace Ui { diff --git a/src/wizard/PageSetSubaddressLookahead.h b/src/wizard/PageSetSubaddressLookahead.h index ae26f08..fe8d86a 100644 --- a/src/wizard/PageSetSubaddressLookahead.h +++ b/src/wizard/PageSetSubaddressLookahead.h @@ -7,7 +7,6 @@ #include #include -#include "appcontext.h" #include "WalletWizard.h" namespace Ui { diff --git a/src/wizard/PageWalletFile.h b/src/wizard/PageWalletFile.h index 82e32bd..73ac30f 100644 --- a/src/wizard/PageWalletFile.h +++ b/src/wizard/PageWalletFile.h @@ -9,8 +9,6 @@ #include #include -#include "appcontext.h" - namespace Ui { class PageWalletFile; } diff --git a/src/wizard/PageWalletRestoreKeys.cpp b/src/wizard/PageWalletRestoreKeys.cpp index 37eb440..4ae5739 100644 --- a/src/wizard/PageWalletRestoreKeys.cpp +++ b/src/wizard/PageWalletRestoreKeys.cpp @@ -11,6 +11,7 @@ #include "WalletWizard.h" #include "constants.h" +#include "libwalletqt/WalletManager.h" PageWalletRestoreKeys::PageWalletRestoreKeys(WizardFields *fields, QWidget *parent) : QWizardPage(parent) diff --git a/src/wizard/PageWalletRestoreKeys.h b/src/wizard/PageWalletRestoreKeys.h index 780052b..832ff7a 100644 --- a/src/wizard/PageWalletRestoreKeys.h +++ b/src/wizard/PageWalletRestoreKeys.h @@ -10,7 +10,6 @@ #include #include -#include "appcontext.h" #include "WalletWizard.h" namespace Ui { diff --git a/src/wizard/PageWalletRestoreSeed.h b/src/wizard/PageWalletRestoreSeed.h index 2aa4c00..394ac72 100644 --- a/src/wizard/PageWalletRestoreSeed.h +++ b/src/wizard/PageWalletRestoreSeed.h @@ -11,7 +11,6 @@ #include #include "utils/textedit.h" -#include "appcontext.h" namespace Ui { class PageWalletRestoreSeed; diff --git a/src/wizard/WalletWizard.h b/src/wizard/WalletWizard.h index b423a6e..975cac8 100644 --- a/src/wizard/WalletWizard.h +++ b/src/wizard/WalletWizard.h @@ -8,10 +8,10 @@ #include #include -#include "appcontext.h" #include "model/WalletKeysFilesModel.h" #include "utils/RestoreHeightLookup.h" #include "utils/config.h" +#include "utils/Seed.h" #include "constants.h" enum WizardMode {