From 0b174d1878d89af07ac107eed96448a5109a1c86 Mon Sep 17 00:00:00 2001 From: Dynex Date: Tue, 5 Dec 2023 06:52:20 +0100 Subject: [PATCH] Fusion GUI Wallet + RPC consolidation fix; + added fusion optimize (it works only for tx with certain amounts); + disabled sending if not synced; + fixed shutdown (fast startup without index rebuild); + fixed zero fee issue in rare cases; + fixed log file creation; + do not create new wallet silently if previous wallet is absent; + added icon for cancelled tx; --- src/DynexCNCore/Core.cpp | 7 --- src/WalletGui/DynexCNWrapper.cpp | 10 ++- src/WalletGui/Settings.cpp | 3 +- src/WalletGui/WalletAdapter.cpp | 80 +++++++++++++++++++----- src/WalletGui/WalletAdapter.h | 6 ++ src/WalletGui/gui/MainWindow.cpp | 2 +- src/WalletGui/gui/SendFrame.cpp | 33 ++++++++-- src/WalletGui/gui/SendFrame.h | 3 + src/WalletGui/gui/TransactionsModel.cpp | 41 +++++++++--- src/WalletGui/gui/TransactionsModel.h | 2 +- src/WalletGui/gui/ui/sendframe.ui | 43 ++++++++++--- src/WalletGui/icons/cancelled.png | Bin 0 -> 153 bytes src/WalletGui/main.cpp | 12 +++- src/WalletGui/resources.qrc | 1 + src/version.h.in | 2 +- 15 files changed, 189 insertions(+), 56 deletions(-) create mode 100644 src/WalletGui/icons/cancelled.png diff --git a/src/DynexCNCore/Core.cpp b/src/DynexCNCore/Core.cpp index e67db520..3db42319 100644 --- a/src/DynexCNCore/Core.cpp +++ b/src/DynexCNCore/Core.cpp @@ -1141,16 +1141,9 @@ bool core::getTransactionsByAddress(const std::string address, std::vector blockchainTransactionHashes; m_blockchain.getTransactionIdsByAddress(address, blockchainTransactionHashes); // Blockchain.cpp - std::vector poolTransactionHashes; - m_mempool.getTransactionIdsByAddress(address, poolTransactionHashes); //TransactionPool.cpp - std::list txs; std::list missed_txs; - if (!poolTransactionHashes.empty()) { - blockchainTransactionHashes.insert(blockchainTransactionHashes.end(), poolTransactionHashes.begin(), poolTransactionHashes.end()); - } - if (blockchainTransactionHashes.empty()) { return false; } diff --git a/src/WalletGui/DynexCNWrapper.cpp b/src/WalletGui/DynexCNWrapper.cpp index 84ecd632..884d85df 100644 --- a/src/WalletGui/DynexCNWrapper.cpp +++ b/src/WalletGui/DynexCNWrapper.cpp @@ -193,7 +193,7 @@ class InprocessNode : DynexCN::INodeObserver, public Node { m_coreConfig(coreConfig), m_logManager(logManager), m_netNodeConfig(netNodeConfig), - m_core(currency, NULL, logManager, true), + m_core(currency, NULL, logManager, false), m_protocolHandler(currency, m_dispatcher, m_core, nullptr, logManager), m_nodeServer(m_dispatcher, m_protocolHandler, logManager), m_node(m_core, m_protocolHandler) { @@ -233,8 +233,14 @@ class InprocessNode : DynexCN::INodeObserver, public Node { }); m_nodeServer.run(); + + //deinitialize components + LoggerAdapter::instance().log("Deinitializing core..."); + m_core.deinit(); m_nodeServer.deinit(); - m_node.shutdown(); + m_core.set_cryptonote_protocol(NULL); + m_protocolHandler.set_p2p_endpoint(NULL); + m_node.shutdown(); } void deinit() override { diff --git a/src/WalletGui/Settings.cpp b/src/WalletGui/Settings.cpp index 4ca1fbe6..5797f7d4 100644 --- a/src/WalletGui/Settings.cpp +++ b/src/WalletGui/Settings.cpp @@ -176,8 +176,7 @@ QDir Settings::getDataDir() const { } QString Settings::getWalletFile() const { - return m_settings.contains("walletFile") ? m_settings.value("walletFile").toString() : - getDataDir().absoluteFilePath(QCoreApplication::applicationName() + ".wallet"); + return m_settings.contains("walletFile") ? m_settings.value("walletFile").toString() : QString(); } QStringList Settings::getRecentWalletsList() const { diff --git a/src/WalletGui/WalletAdapter.cpp b/src/WalletGui/WalletAdapter.cpp index 2016dacd..c76249fe 100644 --- a/src/WalletGui/WalletAdapter.cpp +++ b/src/WalletGui/WalletAdapter.cpp @@ -131,20 +131,21 @@ quint64 WalletAdapter::getPendingBalance() const { void WalletAdapter::open(const QString& _password) { Q_ASSERT(m_wallet == nullptr); Settings::instance().setEncrypted(!_password.isEmpty()); - QString msg = "Opening wallet " + Settings::instance().getWalletFile(); + QString wallet = Settings::instance().getWalletFile(); + QString msg = "Opening wallet " + wallet; Q_EMIT walletStateChangedSignal(msg); m_wallet = NodeAdapter::instance().createWallet(); m_wallet->addObserver(this); - if (QFile::exists(Settings::instance().getWalletFile())) { + if (QFile::exists(wallet)) { if (Settings::instance().getWalletFile().endsWith(".keys")) { if(!importLegacyWallet(_password)) { return; } } - if (openFile(Settings::instance().getWalletFile(), true)) { + if (openFile(wallet, true)) { try { m_wallet->initAndLoad(m_file, _password.toStdString()); } catch (std::system_error&) { @@ -154,19 +155,7 @@ void WalletAdapter::open(const QString& _password) { } } } else { - Settings::instance().setEncrypted(false); - try { -#ifndef GENERATE_DETERMINISTIC - m_wallet->initAndGenerate(""); -#else - m_wallet->initAndGenerateDeterministic(""); - VerifyMnemonicSeedDialog dlg(nullptr); - dlg.exec(); -#endif - } catch (std::system_error&) { - delete m_wallet; - m_wallet = nullptr; - } + QMessageBox::critical(nullptr, tr("Wallet file not found"), wallet, QMessageBox::Ok); } } @@ -373,7 +362,10 @@ bool WalletAdapter::getAccountKeys(DynexCN::AccountKeys& _keys) { void WalletAdapter::sendTransaction(const QVector& _transfers, quint64 _fee, const QString& _paymentId, quint64 _mixin) { Q_CHECK_PTR(m_wallet); - + if (!m_isSynchronized) { + QMessageBox::warning(nullptr, tr("Warning"), tr("Wallet is not synchronized!"), QMessageBox::Ok); + return; + } try { lock(); std::vector vec(_transfers.begin(), _transfers.end()); @@ -620,4 +612,58 @@ DynexCN::AccountKeys WalletAdapter::getKeysFromMnemonicSeed(QString& _seed) cons return keys; } +quint64 WalletAdapter::estimateFusion(quint64 _threshold) { + Q_CHECK_PTR(m_wallet); + try { + return m_wallet->estimateFusion(_threshold); + } catch (std::system_error&) { + } + return 0; +} + +std::list WalletAdapter::getFusionTransfersToSend(quint64 _threshold, size_t _min_input_count, size_t _max_input_count) { + Q_CHECK_PTR(m_wallet); + try { + return m_wallet->selectFusionTransfersToSend(_threshold, _min_input_count, _max_input_count); + } catch (std::system_error&) { + } + return {}; +} + +void WalletAdapter::sendFusionTransaction(const std::list& _fusion_inputs, quint64 _mixin) { + Q_CHECK_PTR(m_wallet); + try { + lock(); + Q_EMIT walletStateChangedSignal(tr("Optimizing wallet")); + m_wallet->sendFusionTransaction(_fusion_inputs, 0, "", _mixin, 0); + } catch (std::system_error&) { + unlock(); + } +} + +bool WalletAdapter::isFusionTransaction(const DynexCN::WalletLegacyTransaction& walletTx) const { + Q_CHECK_PTR(m_wallet); + return m_wallet->isFusionTransaction(walletTx); +} + +void WalletAdapter::optimize(const quint64 mixin) { + if (!isOpen() || !m_isSynchronized) return; + uint64_t fusionThreshold = getActualBalance(); + //size_t fusionReadyCount = estimateFusion(fusionThreshold); + const size_t MAX_FUSION_OUTPUT_COUNT = 4; + size_t estimatedFusionInputsCount = CurrencyAdapter::instance().getCurrency().getApproximateMaximumInputCount(CurrencyAdapter::instance().getCurrency().fusionTxMaxSize(), MAX_FUSION_OUTPUT_COUNT, mixin); + if (estimatedFusionInputsCount < CurrencyAdapter::instance().getCurrency().fusionTxMinInputCount()) { + QMessageBox::warning(nullptr, tr("Optimize"), tr("Anonimity level is too high!"), QMessageBox::Ok); + return; + } + std::list fusionInputs = getFusionTransfersToSend(fusionThreshold, CurrencyAdapter::instance().getCurrency().fusionTxMinInputCount(), estimatedFusionInputsCount); + if (fusionInputs.size() < CurrencyAdapter::instance().getCurrency().fusionTxMinInputCount()) { + QMessageBox::warning(nullptr, tr("Optimize"), tr("Nothing to do!"), QMessageBox::Ok); + return; + } + if (QMessageBox::warning(nullptr, tr("Optimize"), tr("Send fusion transaction?"), QMessageBox::Ok, QMessageBox::Cancel) == QMessageBox::Ok) { + sendFusionTransaction(fusionInputs, mixin); + } +} + } diff --git a/src/WalletGui/WalletAdapter.h b/src/WalletGui/WalletAdapter.h index 71950ec7..c36ef641 100644 --- a/src/WalletGui/WalletAdapter.h +++ b/src/WalletGui/WalletAdapter.h @@ -95,6 +95,12 @@ class WalletAdapter : public QObject, public DynexCN::IWalletLegacyObserver { QString getMnemonicSeed(QString _language) const; DynexCN::AccountKeys getKeysFromMnemonicSeed(QString& _seed) const; + quint64 estimateFusion(quint64 _threshold); + std::list getFusionTransfersToSend(quint64 _threshold, size_t _min_input_count, size_t _max_input_count); + void sendFusionTransaction(const std::list& _fusion_inputs, quint64 _mixin); + bool isFusionTransaction(const DynexCN::WalletLegacyTransaction& walletTx) const; + void optimize(const quint64 mixin); + private: std::fstream m_file; DynexCN::IWalletLegacy* m_wallet; diff --git a/src/WalletGui/gui/MainWindow.cpp b/src/WalletGui/gui/MainWindow.cpp index 26cd9a38..ed5bf122 100644 --- a/src/WalletGui/gui/MainWindow.cpp +++ b/src/WalletGui/gui/MainWindow.cpp @@ -475,7 +475,7 @@ void MainWindow::openRecent() { WalletAdapter::instance().setWalletFile(filePath); WalletAdapter::instance().open(""); } else { - QMessageBox::warning(this, tr("Recent wallet file not found"), tr("The recent wallet file is missing. Probably it was removed."), QMessageBox::Ok); + QMessageBox::warning(this, tr("Wallet file not found"), tr("The recent wallet file is missing. Probably it was removed."), QMessageBox::Ok); } } } diff --git a/src/WalletGui/gui/SendFrame.cpp b/src/WalletGui/gui/SendFrame.cpp index 026bcc05..45968903 100644 --- a/src/WalletGui/gui/SendFrame.cpp +++ b/src/WalletGui/gui/SendFrame.cpp @@ -55,23 +55,41 @@ namespace WalletGui { SendFrame::SendFrame(QWidget* _parent) : QFrame(_parent), m_ui(new Ui::SendFrame) { m_ui->setupUi(this); clearAllClicked(); + m_ui->m_sendButton->setEnabled(false); + m_ui->m_optimizeButton->setEnabled(false); connect(&WalletAdapter::instance(), &WalletAdapter::walletSendTransactionCompletedSignal, this, &SendFrame::sendTransactionCompleted, Qt::QueuedConnection); connect(&WalletAdapter::instance(), &WalletAdapter::walletActualBalanceUpdatedSignal, this, &SendFrame::walletActualBalanceUpdated, Qt::QueuedConnection); + connect(&WalletAdapter::instance(), &WalletAdapter::walletSynchronizationProgressUpdatedSignal, + this, &SendFrame::walletSynchronizationInProgress, Qt::QueuedConnection); + connect(&WalletAdapter::instance(), &WalletAdapter::walletSynchronizationCompletedSignal, this, &SendFrame::walletSynchronized + , Qt::QueuedConnection); + m_ui->m_tickerLabel->setText(CurrencyAdapter::instance().getCurrencyTicker().toUpper()); m_ui->m_feeSpin->setSuffix(" " + CurrencyAdapter::instance().getCurrencyTicker().toUpper()); - double fee = CurrencyAdapter::instance().formatAmount(NodeAdapter::instance().getMinimalFee()).toDouble(); - m_ui->m_feeSpin->setValue(fee); - m_ui->m_feeSpin->setMinimum(fee); - m_ui->m_feeSpin->setMaximum(fee*1000); + //double fee = CurrencyAdapter::instance().formatAmount(NodeAdapter::instance().getMinimalFee()).toDouble(); + //m_ui->m_feeSpin->setValue(fee); + //m_ui->m_feeSpin->setMinimum(fee); + //m_ui->m_feeSpin->setMaximum(fee*1000); } SendFrame::~SendFrame() { } +void SendFrame::walletSynchronizationInProgress() { + m_ui->m_sendButton->setEnabled(false); + m_ui->m_optimizeButton->setEnabled(false); +} + +void SendFrame::walletSynchronized(int _error, const QString& _error_text) { + bool enabled = (WalletAdapter::instance().getActualBalance() > 0 && _error == 0); + m_ui->m_sendButton->setEnabled(enabled); + m_ui->m_optimizeButton->setEnabled(enabled); +} + void SendFrame::addRecipientClicked() { TransferFrame* newTransfer = new TransferFrame(m_ui->m_transfersScrollarea); m_ui->m_send_frame_layout->insertWidget(m_transfers.size(), newTransfer); @@ -103,10 +121,15 @@ void SendFrame::clearAllClicked() { m_ui->m_feeSpin->setValue(CurrencyAdapter::instance().formatAmount(NodeAdapter::instance().getMinimalFee()).toDouble()); } +void SendFrame::optimizeClicked() { + WalletAdapter::instance().optimize(0); +} + void SendFrame::sendClicked() { double minfee = CurrencyAdapter::instance().formatAmount(NodeAdapter::instance().getMinimalFee()).toDouble(); - if (m_ui->m_feeSpin->value() > minfee*1000) { + + if (m_ui->m_feeSpin->value() > 1.0) { // minfee*1000 QCoreApplication::postEvent(&MainWindow::instance(), new ShowMessageEvent(tr("Fee is too high"), QtCriticalMsg)); return; } diff --git a/src/WalletGui/gui/SendFrame.h b/src/WalletGui/gui/SendFrame.h index 8807e286..b639f9a3 100644 --- a/src/WalletGui/gui/SendFrame.h +++ b/src/WalletGui/gui/SendFrame.h @@ -64,9 +64,12 @@ class SendFrame : public QFrame { void sendTransactionCompleted(DynexCN::TransactionId _id, bool _error, const QString& _error_text); void walletActualBalanceUpdated(quint64 _balance); void insertPaymentId(QString _paymentId); + void walletSynchronizationInProgress(); + void walletSynchronized(int _error, const QString& _error_text); Q_SLOT void addRecipientClicked(); Q_SLOT void clearAllClicked(); + Q_SLOT void optimizeClicked(); //Q_SLOT void mixinValueChanged(int _value); Q_SLOT void sendClicked(); }; diff --git a/src/WalletGui/gui/TransactionsModel.cpp b/src/WalletGui/gui/TransactionsModel.cpp index f18ff891..1ad1b90e 100644 --- a/src/WalletGui/gui/TransactionsModel.cpp +++ b/src/WalletGui/gui/TransactionsModel.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include "CurrencyAdapter.h" #include "NodeAdapter.h" @@ -48,7 +49,9 @@ namespace WalletGui { -enum class TransactionType : quint8 {MINED, INPUT, OUTPUT, INOUT}; +enum class TransactionType : quint8 {MINED, INPUT, OUTPUT, INOUT, FUSION}; + +enum class TransactionState : quint8 {ACTIVE, DELETED, SENDING, CANCELLED, FAILED}; namespace { @@ -61,6 +64,7 @@ QPixmap getTransactionIcon(TransactionType _transactionType) { case TransactionType::OUTPUT: return QPixmap(":icons/tx-output"); case TransactionType::INOUT: + case TransactionType::FUSION: return QPixmap(":icons/tx-inout"); default: break; @@ -217,7 +221,7 @@ QVariant TransactionsModel::getDisplayRole(const QModelIndex& _index) const { case COLUMN_ADDRESS: { TransactionType transactionType = static_cast(_index.data(ROLE_TYPE).value()); if (transactionType == TransactionType::INPUT || transactionType == TransactionType::MINED || - transactionType == TransactionType::INOUT) { + transactionType == TransactionType::INOUT || transactionType == TransactionType::FUSION) { return QString(tr("me (%1)").arg(WalletAdapter::instance().getAddress())); } @@ -261,21 +265,32 @@ QVariant TransactionsModel::getDisplayRole(const QModelIndex& _index) const { QVariant TransactionsModel::getDecorationRole(const QModelIndex& _index) const { if(_index.column() == COLUMN_STATE) { quint64 numberOfConfirmations = _index.data(ROLE_NUMBER_OF_CONFIRMATIONS).value(); - if(numberOfConfirmations == 0) { - return QPixmap(":icons/unconfirmed"); + TransactionState transactionState = static_cast(_index.data(ROLE_STATE).value()); + QString file; + if (transactionState != TransactionState::ACTIVE && transactionState != TransactionState::SENDING) { + file = QString(":icons/cancelled"); + } else if (numberOfConfirmations == 0) { + file = QString(":icons/unconfirmed"); } else if(numberOfConfirmations < 2) { - return QPixmap(":icons/clock1"); + file = QString(":icons/clock1"); } else if(numberOfConfirmations < 4) { - return QPixmap(":icons/clock2"); + file = QString(":icons/clock2"); } else if(numberOfConfirmations < 6) { - return QPixmap(":icons/clock3"); + file = QString(":icons/clock3"); } else if(numberOfConfirmations < 8) { - return QPixmap(":icons/clock4"); + file = QString(":icons/clock4"); } else if(numberOfConfirmations < 10) { - return QPixmap(":icons/clock5"); + file = QString(":icons/clock5"); } else { - return QPixmap(":icons/transaction"); + file = QString(":icons/transaction"); + } + QPixmap pixmap; + if (!QPixmapCache::find(file, &pixmap)) { + pixmap.load(file); + QPixmapCache::insert(file, pixmap); } + return pixmap; + } else if (_index.column() == COLUMN_ADDRESS) { return _index.data(ROLE_ICON).value().scaled(20, 20, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } @@ -290,6 +305,10 @@ QVariant TransactionsModel::getAlignmentRole(const QModelIndex& _index) const { QVariant TransactionsModel::getUserRole(const QModelIndex& _index, int _role, DynexCN::TransactionId _transactionId, DynexCN::WalletLegacyTransaction& _transaction, DynexCN::TransferId _transferId, DynexCN::WalletLegacyTransfer& _transfer) const { switch(_role) { + + case ROLE_STATE: + return static_cast(_transaction.state); + case ROLE_DATE: return (_transaction.timestamp > 0 ? QDateTime::fromTime_t(_transaction.timestamp) : QDateTime()); @@ -297,6 +316,8 @@ QVariant TransactionsModel::getUserRole(const QModelIndex& _index, int _role, Dy QString transactionAddress = _index.data(ROLE_ADDRESS).toString(); if(_transaction.isCoinbase) { return static_cast(TransactionType::MINED); + } else if (WalletAdapter::instance().isFusionTransaction(_transaction)) { + return static_cast(TransactionType::FUSION); } else if (!transactionAddress.compare(WalletAdapter::instance().getAddress())) { return static_cast(TransactionType::INOUT); } else if(_transaction.totalAmount < 0) { diff --git a/src/WalletGui/gui/TransactionsModel.h b/src/WalletGui/gui/TransactionsModel.h index 17ca4e7e..a0c419d2 100644 --- a/src/WalletGui/gui/TransactionsModel.h +++ b/src/WalletGui/gui/TransactionsModel.h @@ -59,7 +59,7 @@ class TransactionsModel : public QAbstractItemModel { enum Roles{ ROLE_DATE = Qt::UserRole, ROLE_TYPE, ROLE_HASH, ROLE_ADDRESS, ROLE_AMOUNT, ROLE_PAYMENT_ID, ROLE_ICON, - ROLE_TRANSACTION_ID, ROLE_HEIGHT, ROLE_FEE, ROLE_NUMBER_OF_CONFIRMATIONS, ROLE_COLUMN, ROLE_ROW + ROLE_TRANSACTION_ID, ROLE_HEIGHT, ROLE_FEE, ROLE_NUMBER_OF_CONFIRMATIONS, ROLE_COLUMN, ROLE_ROW, ROLE_STATE }; static TransactionsModel& instance(); diff --git a/src/WalletGui/gui/ui/sendframe.ui b/src/WalletGui/gui/ui/sendframe.ui index 31b661d5..3bb9dbc5 100644 --- a/src/WalletGui/gui/ui/sendframe.ui +++ b/src/WalletGui/gui/ui/sendframe.ui @@ -79,13 +79,13 @@ - 150 + 100 0 - 150 + 100 0 @@ -99,19 +99,19 @@ - 8 + 4 - 0.000100000000000 + 0.0010 - 9999.000000000000000 + 1.000000000000000 - 0.000100000000000 + 0.0001 - 0.000100000000000 + 0.0010 @@ -185,6 +185,17 @@ + + + + Optimize + + + + :/icons/transactions:/icons/transactions + + + @@ -227,6 +238,7 @@ m_paymentIdEdit m_sendButton m_clearAllButton + m_optimizeButton m_addRecipientButton m_transfersScrollarea @@ -266,6 +278,22 @@ + + m_optimizeButton + clicked() + SendFrame + optimizeClicked() + + + 181 + 561 + + + 432 + 294 + + + m_sendButton clicked() @@ -286,6 +314,7 @@ sendClicked() clearAllClicked() + optimizeClicked() addRecipientClicked() mixinValueChanged(int) diff --git a/src/WalletGui/icons/cancelled.png b/src/WalletGui/icons/cancelled.png new file mode 100644 index 0000000000000000000000000000000000000000..703179ef2bddb30bff8674a4be7f71560e765ff6 GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9F5M?jcysy3fAP|(}c z#WBRicons/export.png icons/tx_inout.png icons/unconfirmed.png + icons/cancelled.png images/splash.png diff --git a/src/version.h.in b/src/version.h.in index d897e6db..94eb2628 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -23,4 +23,4 @@ #define CN_CURRENCY_DISPLAY_NAME "DYNEX" #define CN_CURRENCY_TICKER "DNX" -#define CN_WALLET_REV "(non-privacy)" +#define CN_WALLET_REV ".3 (non-privacy)"