From: tobtoht Date: Fri, 17 Nov 2023 13:05:31 +0000 (+0100) Subject: refactor: consolidate wallet_api into libwalletqt [1/?] X-Git-Url: https://git.nutra.tk/v1?a=commitdiff_plain;h=769af188e2345bcc64736db73507a6f9453c040f;p=gamesguru%2Ffeather.git refactor: consolidate wallet_api into libwalletqt [1/?] --- diff --git a/src/CoinsWidget.cpp b/src/CoinsWidget.cpp index 34c82e7e..94825a14 100644 --- a/src/CoinsWidget.cpp +++ b/src/CoinsWidget.cpp @@ -299,7 +299,7 @@ void CoinsWidget::freezeCoins(QStringList &pubkeys) { for (auto &pubkey : pubkeys) { m_wallet->coins()->freeze(pubkey); } - m_wallet->coins()->refresh(m_wallet->currentSubaddressAccount()); + m_wallet->coins()->refresh(); m_wallet->updateBalance(); } @@ -307,7 +307,7 @@ void CoinsWidget::thawCoins(QStringList &pubkeys) { for (auto &pubkey : pubkeys) { m_wallet->coins()->thaw(pubkey); } - m_wallet->coins()->refresh(m_wallet->currentSubaddressAccount()); + m_wallet->coins()->refresh(); m_wallet->updateBalance(); } diff --git a/src/ContactsWidget.cpp b/src/ContactsWidget.cpp index d2a332a6..ecb67fbb 100644 --- a/src/ContactsWidget.cpp +++ b/src/ContactsWidget.cpp @@ -135,9 +135,9 @@ void ContactsWidget::newContact(QString address, QString name) QString address_entry; QString name_entry; for (int i=0; iaddressBook()->getRow(i, [&address_entry, &name_entry](const AddressBookInfo &entry){ - address_entry = entry.address(); - name_entry = entry.description(); + m_wallet->addressBook()->getRow(i, [&address_entry, &name_entry](const ContactRow &entry){ + address_entry = entry.getAddress(); + name_entry = entry.getLabel(); }); if (address == address_entry) { @@ -152,7 +152,7 @@ void ContactsWidget::newContact(QString address, QString name) } } - m_wallet->addressBook()->addRow(address, "", name); + m_wallet->addressBook()->addRow(address, name); } void ContactsWidget::deleteContact() diff --git a/src/HistoryWidget.cpp b/src/HistoryWidget.cpp index 2f80059f..cb298360 100644 --- a/src/HistoryWidget.cpp +++ b/src/HistoryWidget.cpp @@ -86,7 +86,7 @@ void HistoryWidget::showContextMenu(const QPoint &point) { if (!tx) return; bool unconfirmed = tx->isFailed() || tx->isPending(); - if (unconfirmed && tx->direction() != TransactionInfo::Direction_In) { + if (unconfirmed && tx->direction() != TransactionRow::Direction_In) { menu.addAction("Resend transaction", this, &HistoryWidget::onResendTransaction); } diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 7a839717..f2578e29 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -22,7 +22,7 @@ #include "dialog/WalletInfoDialog.h" #include "dialog/WalletCacheDebugDialog.h" #include "libwalletqt/AddressBook.h" -#include "libwalletqt/CoinsInfo.h" +#include "libwalletqt/rows/CoinsInfo.h" #include "libwalletqt/Transfer.h" #include "utils/AppData.h" #include "utils/AsyncTask.h" @@ -502,20 +502,20 @@ void MainWindow::onWalletOpened() { m_wallet->subaddressModel()->setCurrentSubaddressAccount(m_wallet->currentSubaddressAccount()); // history page - m_wallet->history()->refresh(m_wallet->currentSubaddressAccount()); + m_wallet->history()->refresh(); // coins page - m_wallet->coins()->refresh(m_wallet->currentSubaddressAccount()); + m_wallet->coins()->refresh(); 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_wallet->coins(), &Coins::descriptionChanged, [this] { - m_wallet->history()->refresh(m_wallet->currentSubaddressAccount()); + m_wallet->history()->refresh(); }); // Vice versa connect(m_wallet->history(), &TransactionHistory::txNoteChanged, [this] { - m_wallet->coins()->refresh(m_wallet->currentSubaddressAccount()); + m_wallet->coins()->refresh(); }); this->updatePasswordIcon(); @@ -898,7 +898,7 @@ void MainWindow::onTransactionCommitted(bool success, PendingTransaction *tx, co msgBox.exec(); if (msgBox.clickedButton() == showDetailsButton) { this->showHistoryTab(); - TransactionInfo *txInfo = m_wallet->history()->transaction(txid.first()); + TransactionRow *txInfo = m_wallet->history()->transaction(txid.first()); auto *dialog = new TxInfoDialog(m_wallet, txInfo, this); connect(dialog, &TxInfoDialog::resendTranscation, this, &MainWindow::onResendTransaction); dialog->show(); @@ -1145,7 +1145,7 @@ void MainWindow::importContacts() { i.next(); bool addressValid = WalletManager::addressValid(i.value(), m_wallet->nettype()); if(addressValid) { - m_wallet->addressBook()->addRow(i.value(), "", i.key()); + m_wallet->addressBook()->addRow(i.value(), i.key()); inserts++; } } diff --git a/src/dialog/AccountSwitcherDialog.cpp b/src/dialog/AccountSwitcherDialog.cpp index 9977bda3..49c32f6b 100644 --- a/src/dialog/AccountSwitcherDialog.cpp +++ b/src/dialog/AccountSwitcherDialog.cpp @@ -64,7 +64,7 @@ void AccountSwitcherDialog::switchAccount() { return; } - m_wallet->switchSubaddressAccount(row->getRowId()); + m_wallet->switchSubaddressAccount(row->getRow()); } void AccountSwitcherDialog::copyLabel() { @@ -73,7 +73,7 @@ void AccountSwitcherDialog::copyLabel() { return; } - Utils::copyToClipboard(QString::fromStdString(row->getLabel())); + Utils::copyToClipboard(row->getLabel()); } void AccountSwitcherDialog::copyBalance() { @@ -82,7 +82,7 @@ void AccountSwitcherDialog::copyBalance() { return; } - Utils::copyToClipboard(QString::fromStdString(row->getBalance())); + Utils::copyToClipboard(row->getBalance()); } void AccountSwitcherDialog::editLabel() { @@ -93,6 +93,9 @@ void AccountSwitcherDialog::editLabel() { void AccountSwitcherDialog::updateSelection() { QModelIndex index = m_model->index(m_wallet->currentSubaddressAccount(), 0); + if (!index.isValid()) { + return; + } ui->accounts->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); } @@ -111,7 +114,7 @@ void AccountSwitcherDialog::showContextMenu(const QPoint &point) { menu->popup(ui->accounts->viewport()->mapToGlobal(point)); } -Monero::SubaddressAccountRow* AccountSwitcherDialog::currentEntry() { +AccountRow* AccountSwitcherDialog::currentEntry() { QModelIndex index = m_proxyModel->mapToSource(ui->accounts->currentIndex()); return m_wallet->subaddressAccountModel()->entryFromIndex(index); } diff --git a/src/dialog/AccountSwitcherDialog.h b/src/dialog/AccountSwitcherDialog.h index 5e3c6bb3..568041e2 100644 --- a/src/dialog/AccountSwitcherDialog.h +++ b/src/dialog/AccountSwitcherDialog.h @@ -34,7 +34,7 @@ private: void copyBalance(); void editLabel(); - Monero::SubaddressAccountRow* currentEntry(); + AccountRow* currentEntry(); QScopedPointer ui; Wallet *m_wallet; diff --git a/src/dialog/AccountSwitcherDialog.ui b/src/dialog/AccountSwitcherDialog.ui index 1b203cfd..e23ccc74 100644 --- a/src/dialog/AccountSwitcherDialog.ui +++ b/src/dialog/AccountSwitcherDialog.ui @@ -16,9 +16,6 @@ - - Qt::NoFocus - false diff --git a/src/dialog/OutputInfoDialog.h b/src/dialog/OutputInfoDialog.h index 729d5d06..0a4080f0 100644 --- a/src/dialog/OutputInfoDialog.h +++ b/src/dialog/OutputInfoDialog.h @@ -8,7 +8,7 @@ #include "components.h" #include "libwalletqt/Coins.h" -#include "libwalletqt/CoinsInfo.h" +#include "libwalletqt/rows/CoinsInfo.h" namespace Ui { class OutputInfoDialog; diff --git a/src/dialog/OutputSweepDialog.h b/src/dialog/OutputSweepDialog.h index 7907b2cc..9372b9ef 100644 --- a/src/dialog/OutputSweepDialog.h +++ b/src/dialog/OutputSweepDialog.h @@ -7,7 +7,7 @@ #include #include "components.h" -#include "libwalletqt/CoinsInfo.h" +#include "libwalletqt/rows/CoinsInfo.h" namespace Ui { class OutputSweepDialog; diff --git a/src/dialog/TxInfoDialog.cpp b/src/dialog/TxInfoDialog.cpp index 4b5e3d83..3d2ddc2a 100644 --- a/src/dialog/TxInfoDialog.cpp +++ b/src/dialog/TxInfoDialog.cpp @@ -10,14 +10,14 @@ #include "config.h" #include "constants.h" #include "libwalletqt/Coins.h" -#include "libwalletqt/CoinsInfo.h" +#include "libwalletqt/rows/CoinsInfo.h" #include "libwalletqt/TransactionHistory.h" #include "libwalletqt/Transfer.h" #include "libwalletqt/WalletManager.h" #include "utils/Icons.h" #include "utils/Utils.h" -TxInfoDialog::TxInfoDialog(Wallet *wallet, TransactionInfo *txInfo, QWidget *parent) +TxInfoDialog::TxInfoDialog(Wallet *wallet, TransactionRow *txInfo, QWidget *parent) : QDialog(parent) , ui(new Ui::TxInfoDialog) , m_wallet(wallet) @@ -41,7 +41,7 @@ TxInfoDialog::TxInfoDialog(Wallet *wallet, TransactionInfo *txInfo, QWidget *par this->setData(txInfo); - if ((txInfo->isFailed() || txInfo->isPending()) && txInfo->direction() != TransactionInfo::Direction_In) { + if ((txInfo->isFailed() || txInfo->isPending()) && txInfo->direction() != TransactionRow::Direction_In) { connect(ui->btn_rebroadcastTx, &QPushButton::pressed, [this]{ emit resendTranscation(m_txid); }); @@ -49,7 +49,7 @@ TxInfoDialog::TxInfoDialog(Wallet *wallet, TransactionInfo *txInfo, QWidget *par ui->btn_rebroadcastTx->hide(); } - if (txInfo->direction() == TransactionInfo::Direction_In) { + if (txInfo->direction() == TransactionRow::Direction_In) { ui->btn_CopyTxKey->setDisabled(true); ui->btn_CopyTxKey->setToolTip("No tx secret key available for incoming transactions."); } @@ -106,7 +106,7 @@ void TxInfoDialog::adjustHeight(QTextEdit *textEdit, qreal docHeight) { textEdit->verticalScrollBar()->hide(); } -void TxInfoDialog::setData(TransactionInfo *tx) { +void TxInfoDialog::setData(TransactionRow *tx) { QString blockHeight = QString::number(tx->blockHeight()); if (tx->isFailed()) { @@ -131,13 +131,13 @@ void TxInfoDialog::setData(TransactionInfo *tx) { ui->label_lock->setText("Lock: Outputs are spendable"); } - QString direction = tx->direction() == TransactionInfo::Direction_In ? "received" : "sent"; + QString direction = tx->direction() == TransactionRow::Direction_In ? "received" : "sent"; ui->label_amount->setText(QString("Amount %1: %2 XMR").arg(direction, tx->displayAmount())); QString fee; if (tx->isCoinbase()) fee = "Not applicable"; - else if (tx->direction() == TransactionInfo::Direction_In) + else if (tx->direction() == TransactionRow::Direction_In) fee = "Paid by sender"; else if (tx->fee().isEmpty()) fee = "N/A"; @@ -149,7 +149,7 @@ void TxInfoDialog::setData(TransactionInfo *tx) { } void TxInfoDialog::updateData() { - TransactionInfo *tx = m_wallet->history()->transaction(m_txid); + TransactionRow *tx = m_wallet->history()->transaction(m_txid); if (!tx) return; this->setData(tx); } diff --git a/src/dialog/TxInfoDialog.h b/src/dialog/TxInfoDialog.h index b80e9e4a..4ad0cf00 100644 --- a/src/dialog/TxInfoDialog.h +++ b/src/dialog/TxInfoDialog.h @@ -10,6 +10,7 @@ #include #include "dialog/TxProofDialog.h" +#include "libwalletqt/rows/TransactionRow.h" namespace Ui { class TxInfoDialog; @@ -20,7 +21,7 @@ class TxInfoDialog : public QDialog Q_OBJECT public: - explicit TxInfoDialog(Wallet *wallet, TransactionInfo *txInfo, QWidget *parent = nullptr); + explicit TxInfoDialog(Wallet *wallet, TransactionRow *txInfo, QWidget *parent = nullptr); ~TxInfoDialog() override; signals: @@ -30,14 +31,14 @@ private: void copyTxID(); void copyTxKey(); void createTxProof(); - void setData(TransactionInfo *tx); + void setData(TransactionRow *tx); void updateData(); void adjustHeight(QTextEdit *textEdit, qreal docHeight); void viewOnBlockExplorer(); QScopedPointer ui; Wallet *m_wallet; - TransactionInfo *m_txInfo; + TransactionRow *m_txInfo; TxProofDialog *m_txProofDialog; QString m_txid; }; diff --git a/src/dialog/TxProofDialog.cpp b/src/dialog/TxProofDialog.cpp index 4a3650d7..b865ac17 100644 --- a/src/dialog/TxProofDialog.cpp +++ b/src/dialog/TxProofDialog.cpp @@ -10,7 +10,7 @@ #include "utils/Icons.h" #include "utils/Utils.h" -TxProofDialog::TxProofDialog(QWidget *parent, Wallet *wallet, TransactionInfo *txInfo) +TxProofDialog::TxProofDialog(QWidget *parent, Wallet *wallet, TransactionRow *txInfo) : WindowModalDialog(parent) , ui(new Ui::TxProofDialog) , m_wallet(wallet) @@ -70,7 +70,7 @@ void TxProofDialog::selectSpendProof() { m_mode = Mode::SpendProof; this->resetFrames(); - if (m_direction == TransactionInfo::Direction_In) { + if (m_direction == TransactionRow::Direction_In) { this->showWarning("Your wallet did not construct this transaction. Creating a SpendProof is not possible."); return; } @@ -107,7 +107,7 @@ void TxProofDialog::selectInProof() { m_mode = Mode::InProof; this->resetFrames(); - if (m_direction == TransactionInfo::Direction_Out) { + if (m_direction == TransactionRow::Direction_Out) { this->showWarning("Can't create InProofs for outgoing transactions."); return; } diff --git a/src/dialog/TxProofDialog.h b/src/dialog/TxProofDialog.h index 8f14d977..215946a7 100644 --- a/src/dialog/TxProofDialog.h +++ b/src/dialog/TxProofDialog.h @@ -7,8 +7,8 @@ #include #include "components.h" -#include "libwalletqt/TransactionInfo.h" #include "libwalletqt/Wallet.h" +#include "rows/TransactionRow.h" namespace Ui { class TxProofDialog; @@ -19,7 +19,7 @@ class TxProofDialog : public WindowModalDialog Q_OBJECT public: - explicit TxProofDialog(QWidget *parent, Wallet *wallet, TransactionInfo *txid); + explicit TxProofDialog(QWidget *parent, Wallet *wallet, TransactionRow *txid); ~TxProofDialog() override; void setTxId(const QString &txid); void getTxKey(); @@ -52,7 +52,7 @@ private: QString m_txid; QString m_txKey; Mode m_mode; - TransactionInfo::Direction m_direction; + TransactionRow::Direction m_direction; }; #endif //FEATHER_TXPROOFDIALOG_H diff --git a/src/libwalletqt/AddressBook.cpp b/src/libwalletqt/AddressBook.cpp index d4230453..607bd92d 100644 --- a/src/libwalletqt/AddressBook.cpp +++ b/src/libwalletqt/AddressBook.cpp @@ -4,48 +4,54 @@ #include "AddressBook.h" #include -AddressBook::AddressBook(Monero::AddressBook *abImpl, QObject *parent) - : QObject(parent), m_addressBookImpl(abImpl) +AddressBook::AddressBook(Wallet *wallet, tools::wallet2 *wallet2, QObject *parent) + : QObject(parent) + , m_wallet(wallet) + , m_wallet2(wallet2) { - getAll(); + this->refresh(); } QString AddressBook::errorString() const { - return QString::fromStdString(m_addressBookImpl->errorString()); + return m_errorString; } -int AddressBook::errorCode() const +AddressBook::ErrorCode AddressBook::errorCode() const { - return m_addressBookImpl->errorCode(); + return m_errorCode; } -void AddressBook::getAll() +void AddressBook::refresh() { emit refreshStarted(); - { - QWriteLocker locker(&m_lock); - - qDeleteAll(m_rows); + clearRows(); - m_addresses.clear(); - m_rows.clear(); + // Fetch from Wallet2 and create vector of AddressBookRow objects + std::vector rows = m_wallet2->get_address_book(); + for (qsizetype i = 0; i < rows.size(); ++i) { + tools::wallet2::address_book_row *row = &rows.at(i); - for (auto &abr: m_addressBookImpl->getAll()) { - m_addresses.insert(QString::fromStdString(abr->getAddress()), m_rows.size()); + std::string address; + if (row->m_has_payment_id) + address = cryptonote::get_account_integrated_address_as_str(m_wallet2->nettype(), row->m_address, row->m_payment_id); + else + address = get_account_address_as_str(m_wallet2->nettype(), row->m_is_subaddress, row->m_address); - m_rows.append(new AddressBookInfo(abr, this)); - } + auto* abr = new ContactRow{this, + i, + QString::fromStdString(address), + QString::fromStdString(row->m_description)}; + m_rows.push_back(abr); } + emit refreshFinished(); } -bool AddressBook::getRow(int index, std::function callback) const +bool AddressBook::getRow(int index, std::function callback) const { - QReadLocker locker(&m_lock); - if (index < 0 || index >= m_rows.size()) { return false; @@ -55,88 +61,58 @@ bool AddressBook::getRow(int index, std::function call return true; } -bool AddressBook::addRow(const QString &address, const QString &payment_id, const QString &description) +bool AddressBook::addRow(const QString &address, const QString &description) { - // virtual bool addRow(const std::string &dst_addr , const std::string &payment_id, const std::string &description) = 0; - bool result; + m_errorString = ""; - { - QWriteLocker locker(&m_lock); - - result = m_addressBookImpl->addRow(address.toStdString(), payment_id.toStdString(), description.toStdString()); - } - - if (result) - { - getAll(); + cryptonote::address_parse_info info; + if (!cryptonote::get_account_address_from_str(info, m_wallet2->nettype(), address.toStdString())) { + m_errorString = tr("Invalid destination address"); + m_errorCode = Invalid_Address; + return false; } - return result; + bool r = m_wallet2->add_address_book_row(info.address, info.has_payment_id ? &info.payment_id : nullptr, description.toStdString(), info.is_subaddress); + if (r) + refresh(); + else + m_errorCode = General_Error; + return r; } -void AddressBook::setDescription(int index, const QString &description) { - bool result; - - { - QWriteLocker locker(&m_lock); +bool AddressBook::setDescription(int index, const QString &description) { + m_errorString = ""; - result = m_addressBookImpl->setDescription(index, description.toStdString()); + const auto ab = m_wallet2->get_address_book(); + if (index >= ab.size()){ + return false; } - if (result) - { - getAll(); - emit descriptionChanged(); - } + tools::wallet2::address_book_row entry = ab[index]; + entry.m_description = description.toStdString(); + bool r = m_wallet2->set_address_book_row(index, entry.m_address, entry.m_has_payment_id ? &entry.m_payment_id : nullptr, entry.m_description, entry.m_is_subaddress); + if (r) + refresh(); + else + m_errorCode = General_Error; + return r; } bool AddressBook::deleteRow(int rowId) { - bool result; - - { - QWriteLocker locker(&m_lock); - - result = m_addressBookImpl->deleteRow(rowId); - } - - // Fetch new data from wallet2. - if (result) - { - getAll(); - } - - return result; -} - -quint64 AddressBook::count() const -{ - QReadLocker locker(&m_lock); - - return m_rows.size(); + bool r = m_wallet2->delete_address_book_row(rowId); + if (r) + refresh(); + return r; } -QString AddressBook::getDescription(const QString &address) const +qsizetype AddressBook::count() const { - QReadLocker locker(&m_lock); - - const QMap::const_iterator it = m_addresses.find(address); - if (it == m_addresses.end()) - { - return {}; - } - return m_rows.value(*it)->description(); + return m_rows.length(); } -QString AddressBook::getAddress(const QString &description) const +void AddressBook::clearRows() { - QReadLocker locker(&m_lock); - - for (const auto &row : m_rows) { - if (row->description() == description) { - return row->address(); - } - } - - return QString(); + qDeleteAll(m_rows); + m_rows.clear(); } \ No newline at end of file diff --git a/src/libwalletqt/AddressBook.h b/src/libwalletqt/AddressBook.h index 2b7fd30d..b9e7f209 100644 --- a/src/libwalletqt/AddressBook.h +++ b/src/libwalletqt/AddressBook.h @@ -5,43 +5,44 @@ #define ADDRESSBOOK_H #include -#include "AddressBookInfo.h" #include #include #include #include #include +#include "rows/ContactRow.h" +#include "Wallet.h" +#include "wallet/wallet2.h" + namespace Monero { struct AddressBook; } -class AddressBookRow; class AddressBook : public QObject { Q_OBJECT -public: - Q_INVOKABLE bool getRow(int index, std::function callback) const; - Q_INVOKABLE bool addRow(const QString &address, const QString &payment_id, const QString &description); - Q_INVOKABLE bool deleteRow(int rowId); - Q_INVOKABLE void setDescription(int index, const QString &label); - quint64 count() const; - Q_INVOKABLE QString errorString() const; - Q_INVOKABLE int errorCode() const; - Q_INVOKABLE QString getDescription(const QString &address) const; - Q_INVOKABLE QString getAddress(const QString &description) const; +public: enum ErrorCode { Status_Ok, General_Error, Invalid_Address, Invalid_Payment_Id }; - Q_ENUM(ErrorCode); -private: - void getAll(); + bool getRow(int index, std::function callback) const; + bool addRow(const QString &address, const QString &description); + bool deleteRow(int rowId); + bool setDescription(int index, const QString &label); + qsizetype count() const; + QString errorString() const; + ErrorCode errorCode() const; + + void refresh(); + void clearRows(); + signals: void refreshStarted() const; @@ -49,12 +50,15 @@ signals: void descriptionChanged() const; private: - explicit AddressBook(Monero::AddressBook * abImpl, QObject *parent); + explicit AddressBook(Wallet *wallet, tools::wallet2 *wallet2, QObject *parent); friend class Wallet; - Monero::AddressBook * m_addressBookImpl; - mutable QReadWriteLock m_lock; - QList m_rows; - QMap m_addresses; + + Wallet *m_wallet; + tools::wallet2 *m_wallet2; + QList m_rows; + + QString m_errorString; + ErrorCode m_errorCode; }; #endif // ADDRESSBOOK_H diff --git a/src/libwalletqt/AddressBookInfo.cpp b/src/libwalletqt/AddressBookInfo.cpp deleted file mode 100644 index b2e4ee72..00000000 --- a/src/libwalletqt/AddressBookInfo.cpp +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// SPDX-FileCopyrightText: 2020-2023 The Monero Project - -#include "AddressBookInfo.h" - -QString AddressBookInfo::address() const { - return m_address; -} - -QString AddressBookInfo::description() const { - return m_description; -} - -AddressBookInfo::AddressBookInfo(const Monero::AddressBookRow *pimpl, QObject *parent) - : QObject(parent) - , m_address(QString::fromStdString(pimpl->getAddress())) - , m_description(QString::fromStdString(pimpl->getDescription())) -{ - -} diff --git a/src/libwalletqt/AddressBookInfo.h b/src/libwalletqt/AddressBookInfo.h deleted file mode 100644 index cd653bf2..00000000 --- a/src/libwalletqt/AddressBookInfo.h +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// SPDX-FileCopyrightText: 2020-2023 The Monero Project - -#ifndef FEATHER_ADDRESSBOOKINFO_H -#define FEATHER_ADDRESSBOOKINFO_H - -#include -#include - -class AddressBookInfo : public QObject { - Q_OBJECT - Q_PROPERTY(QString address READ address); - Q_PROPERTY(QString description READ description); - -public: - QString address() const; - QString description() const; - -private: - explicit AddressBookInfo(const Monero::AddressBookRow *pimpl, QObject *parent = nullptr); - - friend class AddressBook; - QString m_address; - QString m_description; -}; - - -#endif //FEATHER_ADDRESSBOOKINFO_H diff --git a/src/libwalletqt/Coins.cpp b/src/libwalletqt/Coins.cpp index fb609666..3f74fe45 100644 --- a/src/libwalletqt/Coins.cpp +++ b/src/libwalletqt/Coins.cpp @@ -2,50 +2,80 @@ // SPDX-FileCopyrightText: 2020-2023 The Monero Project #include "Coins.h" +#include "rows/CoinsInfo.h" -#include - -#include "CoinsInfo.h" - -#include +Coins::Coins(Wallet *wallet, tools::wallet2 *wallet2, QObject *parent) + : QObject(parent) + , m_wallet(wallet) + , m_wallet2(wallet2) +{ +} bool Coins::coin(int index, std::function callback) { QReadLocker locker(&m_lock); - if (index < 0 || index >= m_tinfo.size()) { + if (index < 0 || index >= m_rows.size()) { qCritical("%s: no transaction info for index %d", __FUNCTION__, index); - qCritical("%s: there's %d transactions in backend", __FUNCTION__, m_pimpl->count()); + qCritical("%s: there's %lld transactions in backend", __FUNCTION__, m_rows.count()); return false; } - callback(*m_tinfo.value(index)); + callback(*m_rows.value(index)); return true; } CoinsInfo* Coins::coin(int index) { - return m_tinfo.value(index); + return m_rows.value(index); } -void Coins::refresh(quint32 accountIndex) +void Coins::refresh() { emit refreshStarted(); + boost::shared_lock transfers_lock(m_wallet2->m_transfers_mutex); + { QWriteLocker locker(&m_lock); - qDeleteAll(m_tinfo); - m_tinfo.clear(); + clearRows(); + uint32_t account = m_wallet->currentSubaddressAccount(); + + for (size_t i = 0; i < m_wallet2->get_num_transfer_details(); ++i) + { + const tools::wallet2::transfer_details &td = m_wallet2->get_transfer_details(i); - m_pimpl->refresh(); - for (const auto i : m_pimpl->getAll()) { - if (i->subaddrAccount() != accountIndex) { + if (td.m_subaddr_index.major != account) { continue; } - m_tinfo.append(new CoinsInfo(i, this)); + auto ci = new CoinsInfo(this); + ci->m_blockHeight = td.m_block_height; + ci->m_hash = QString::fromStdString(epee::string_tools::pod_to_hex(td.m_txid)); + ci->m_internalOutputIndex = td.m_internal_output_index; + ci->m_globalOutputIndex = td.m_global_output_index; + ci->m_spent = td.m_spent; + ci->m_frozen = td.m_frozen; + ci->m_spentHeight = td.m_spent_height; + ci->m_amount = td.m_amount; + ci->m_rct = td.m_rct; + ci->m_keyImageKnown = td.m_key_image_known; + ci->m_pkIndex = td.m_pk_index; + ci->m_subaddrIndex = td.m_subaddr_index.minor; + ci->m_subaddrAccount = td.m_subaddr_index.major; + ci->m_address = QString::fromStdString(m_wallet2->get_subaddress_as_str(td.m_subaddr_index)); // todo: this is expensive, cache maybe? + ci->m_addressLabel = QString::fromStdString(m_wallet2->get_subaddress_label(td.m_subaddr_index)); + ci->m_keyImage = QString::fromStdString(epee::string_tools::pod_to_hex(td.m_key_image)); + ci->m_unlockTime = td.m_tx.unlock_time; + ci->m_unlocked = m_wallet2->is_transfer_unlocked(td); + ci->m_pubKey = QString::fromStdString(epee::string_tools::pod_to_hex(td.get_public_key())); + ci->m_coinbase = td.m_tx.vin.size() == 1 && td.m_tx.vin[0].type() == typeid(cryptonote::txin_gen); + ci->m_description = m_wallet->getCacheAttribute(QString("coin.description:%1").arg(ci->m_pubKey)); + ci->m_change = m_wallet2->is_change(td); + + m_rows.push_back(ci); } } @@ -56,9 +86,9 @@ void Coins::refreshUnlocked() { QWriteLocker locker(&m_lock); - for (CoinsInfo* c : m_tinfo) { + for (CoinsInfo* c : m_rows) { if (!c->unlocked()) { - bool unlocked = m_pimpl->isTransferUnlocked(c->unlockTime(), c->blockHeight()); + bool unlocked = m_wallet2->is_transfer_unlocked(c->unlockTime(), c->blockHeight()); c->setUnlocked(unlocked); } } @@ -68,18 +98,50 @@ quint64 Coins::count() const { QReadLocker locker(&m_lock); - return m_tinfo.count(); + return m_rows.length(); } -void Coins::freeze(QString &publicKey) const +void Coins::freeze(QString &publicKey) { - m_pimpl->setFrozen(publicKey.toStdString()); + crypto::public_key pk; + if (!epee::string_tools::hex_to_pod(publicKey.toStdString(), pk)) + { + qWarning() << "Invalid public key: " << publicKey; + return; + } + + try + { + m_wallet2->freeze(pk); + refresh(); + } + catch (const std::exception& e) + { + qWarning() << "freeze: " << e.what(); + } + emit coinFrozen(); } -void Coins::thaw(QString &publicKey) const +void Coins::thaw(QString &publicKey) { - m_pimpl->thaw(publicKey.toStdString()); + crypto::public_key pk; + if (!epee::string_tools::hex_to_pod(publicKey.toStdString(), pk)) + { + qWarning() << "Invalid public key: " << publicKey; + return; + } + + try + { + m_wallet2->thaw(pk); + refresh(); + } + catch (const std::exception& e) + { + qWarning() << "thaw: " << e.what(); + } + emit coinThawed(); } @@ -111,14 +173,12 @@ QVector Coins::coinsFromKeyImage(const QStringList &keyimages) { void Coins::setDescription(const QString &publicKey, quint32 accountIndex, const QString &description) { - m_pimpl->setDescription(publicKey.toStdString(), description.toStdString()); - this->refresh(accountIndex); + m_wallet->setCacheAttribute(QString("coin.description:%1").arg(publicKey), description); + this->refresh(); emit descriptionChanged(); } -Coins::Coins(Monero::Coins *pimpl, QObject *parent) - : QObject(parent) - , m_pimpl(pimpl) -{ - +void Coins::clearRows() { + qDeleteAll(m_rows); + m_rows.clear(); } \ No newline at end of file diff --git a/src/libwalletqt/Coins.h b/src/libwalletqt/Coins.h index 4ab96880..8c6e9cdd 100644 --- a/src/libwalletqt/Coins.h +++ b/src/libwalletqt/Coins.h @@ -12,6 +12,9 @@ #include #include +#include "Wallet.h" +#include "wallet/wallet2.h" + namespace Monero { struct TransactionHistory; } @@ -25,15 +28,16 @@ Q_OBJECT public: bool coin(int index, std::function callback); CoinsInfo * coin(int index); - void refresh(quint32 accountIndex); + void refresh(); void refreshUnlocked(); - void freeze(QString &publicKey) const; - void thaw(QString &publicKey) const; + void freeze(QString &publicKey); + void thaw(QString &publicKey); QVector coins_from_txid(const QString &txid); QVector coinsFromKeyImage(const QStringList &keyimages); void setDescription(const QString &publicKey, quint32 accountIndex, const QString &description); quint64 count() const; + void clearRows(); signals: void refreshStarted() const; @@ -43,13 +47,16 @@ signals: void descriptionChanged() const; private: - explicit Coins(Monero::Coins * pimpl, QObject *parent = nullptr); + explicit Coins(Wallet *wallet, tools::wallet2 *wallet2, QObject *parent = nullptr); private: friend class Wallet; + + Wallet *m_wallet; + tools::wallet2 *m_wallet2; + QList m_rows; + mutable QReadWriteLock m_lock; - Monero::Coins * m_pimpl; - mutable QList m_tinfo; }; #endif //FEATHER_COINS_H diff --git a/src/libwalletqt/Ring.h b/src/libwalletqt/Ring.h index ceb27147..aa0d558b 100644 --- a/src/libwalletqt/Ring.h +++ b/src/libwalletqt/Ring.h @@ -12,14 +12,15 @@ class Ring : public QObject { Q_OBJECT - Q_PROPERTY(QString keyImage READ keyImage) - Q_PROPERTY(std::vector ringMembers READ ringMembers) -private: + +public: explicit Ring(QString _keyImage, std::vector _ringMembers, QObject *parent = nullptr): QObject(parent), m_keyImage(std::move(_keyImage)), m_ringMembers(std::move(_ringMembers)) {}; + private: friend class TransactionInfo; QString m_keyImage; std::vector m_ringMembers; + public: QString keyImage() const { return m_keyImage; } std::vector ringMembers() const { return m_ringMembers; } diff --git a/src/libwalletqt/Subaddress.cpp b/src/libwalletqt/Subaddress.cpp index 13374c90..d33072dc 100644 --- a/src/libwalletqt/Subaddress.cpp +++ b/src/libwalletqt/Subaddress.cpp @@ -159,8 +159,8 @@ void Subaddress::clearRows() { SubaddressRow* Subaddress::row(int index) const { return m_rows.value(index); -}; +} QString Subaddress::getError() const { return m_errorString; -}; \ No newline at end of file +} \ No newline at end of file diff --git a/src/libwalletqt/SubaddressAccount.cpp b/src/libwalletqt/SubaddressAccount.cpp index 60f6fd04..16db72f4 100644 --- a/src/libwalletqt/SubaddressAccount.cpp +++ b/src/libwalletqt/SubaddressAccount.cpp @@ -4,31 +4,15 @@ #include "SubaddressAccount.h" #include -SubaddressAccount::SubaddressAccount(Monero::SubaddressAccount *subaddressAccountImpl, QObject *parent) - : QObject(parent), m_subaddressAccountImpl(subaddressAccountImpl) +SubaddressAccount::SubaddressAccount(Wallet *wallet, tools::wallet2 *wallet2, QObject *parent) + : QObject(parent) + , m_wallet(wallet) + , m_wallet2(wallet2) { - getAll(); } -void SubaddressAccount::getAll() const +bool SubaddressAccount::getRow(int index, std::function callback) const { - emit refreshStarted(); - - { - QWriteLocker locker(&m_lock); - m_rows.clear(); - for (auto &row: m_subaddressAccountImpl->getAll()) { - m_rows.append(row); - } - } - - emit refreshFinished(); -} - -bool SubaddressAccount::getRow(int index, std::function callback) const -{ - QReadLocker locker(&m_lock); - if (index < 0 || index >= m_rows.size()) { return false; @@ -38,32 +22,50 @@ bool SubaddressAccount::getRow(int index, std::functionaddRow(label.toStdString()); - getAll(); + m_wallet2->add_subaddress_account(label.toStdString()); + refresh(); } -void SubaddressAccount::setLabel(quint32 accountIndex, const QString &label) const +void SubaddressAccount::setLabel(quint32 accountIndex, const QString &label) { - m_subaddressAccountImpl->setLabel(accountIndex, label.toStdString()); - getAll(); + m_wallet2->set_subaddress_label({accountIndex, 0}, label.toStdString()); + refresh(); } -void SubaddressAccount::refresh() const +void SubaddressAccount::refresh() { - m_subaddressAccountImpl->refresh(); - getAll(); + emit refreshStarted(); + + this->clearRows(); + for (uint32_t i = 0; i < m_wallet2->get_num_subaddress_accounts(); ++i) + { + auto *row = new AccountRow{this, + i, + QString::fromStdString(m_wallet2->get_subaddress_as_str({i,0})), + QString::fromStdString(m_wallet2->get_subaddress_label({i,0})), + m_wallet2->balance(i, false), + m_wallet2->unlocked_balance(i, false)}; + + m_rows.append(row); + } + + emit refreshFinished(); } -quint64 SubaddressAccount::count() const +qsizetype SubaddressAccount::count() const { - QReadLocker locker(&m_lock); + return m_rows.length(); +} - return m_rows.size(); +void SubaddressAccount::clearRows() +{ + qDeleteAll(m_rows); + m_rows.clear(); } -Monero::SubaddressAccountRow* SubaddressAccount::row(int index) const +AccountRow* SubaddressAccount::row(int index) const { return m_rows.value(index); } \ No newline at end of file diff --git a/src/libwalletqt/SubaddressAccount.h b/src/libwalletqt/SubaddressAccount.h index fd3add9f..28957b40 100644 --- a/src/libwalletqt/SubaddressAccount.h +++ b/src/libwalletqt/SubaddressAccount.h @@ -12,30 +12,40 @@ #include #include +#include + +#include "Wallet.h" +#include "rows/AccountRow.h" + class SubaddressAccount : public QObject { Q_OBJECT + public: - Q_INVOKABLE void getAll() const; - Q_INVOKABLE bool getRow(int index, std::function callback) const; - Q_INVOKABLE void addRow(const QString &label) const; - Q_INVOKABLE void setLabel(quint32 accountIndex, const QString &label) const; - Q_INVOKABLE void refresh() const; - quint64 count() const; - Monero::SubaddressAccountRow* row(int index) const; + void getAll() const; + bool getRow(int index, std::function callback) const; + void addRow(const QString &label); + + void setLabel(quint32 accountIndex, const QString &label); + + void refresh(); + + qsizetype count() const; + void clearRows(); + + AccountRow* row(int index) const; signals: void refreshStarted() const; void refreshFinished() const; -public slots: - private: - explicit SubaddressAccount(Monero::SubaddressAccount * subaddressAccountImpl, QObject *parent); + explicit SubaddressAccount(Wallet *wallet, tools::wallet2 *wallet2, QObject *parent); friend class Wallet; - mutable QReadWriteLock m_lock; - Monero::SubaddressAccount * m_subaddressAccountImpl; - mutable QList m_rows; + + Wallet *m_wallet; + tools::wallet2 *m_wallet2; + QList m_rows; }; #endif // SUBADDRESSACCOUNT_H diff --git a/src/libwalletqt/TransactionHistory.cpp b/src/libwalletqt/TransactionHistory.cpp index 2a5877e6..d3bf977f 100644 --- a/src/libwalletqt/TransactionHistory.cpp +++ b/src/libwalletqt/TransactionHistory.cpp @@ -2,48 +2,49 @@ // SPDX-FileCopyrightText: 2020-2023 The Monero Project #include "TransactionHistory.h" -#include "TransactionInfo.h" #include "utils/Utils.h" #include "utils/AppData.h" #include "utils/config.h" #include "constants.h" #include "WalletManager.h" +#include "Transfer.h" +#include "Ring.h" -bool TransactionHistory::transaction(int index, std::function callback) +bool TransactionHistory::transaction(int index, std::function callback) { QReadLocker locker(&m_lock); - if (index < 0 || index >= m_tinfo.size()) { + if (index < 0 || index >= m_rows.size()) { qCritical("%s: no transaction info for index %d", __FUNCTION__, index); - qCritical("%s: there's %d transactions in backend", __FUNCTION__, m_pimpl->count()); + qCritical("%s: there's %d transactions in backend", __FUNCTION__, this->count()); return false; } - callback(*m_tinfo.value(index)); + callback(*m_rows.value(index)); return true; } -TransactionInfo* TransactionHistory::transaction(const QString &id) +TransactionRow* TransactionHistory::transaction(const QString &id) { QReadLocker locker(&m_lock); - auto itr = std::find_if(m_tinfo.begin(), m_tinfo.end(), - [&](const TransactionInfo * ti) { + auto itr = std::find_if(m_rows.begin(), m_rows.end(), + [&](const TransactionRow * ti) { return ti->hash() == id; }); - return itr != m_tinfo.end() ? *itr : nullptr; + return itr != m_rows.end() ? *itr : nullptr; } -TransactionInfo* TransactionHistory::transaction(int index) +TransactionRow* TransactionHistory::transaction(int index) { - if (index < 0 || index >= m_tinfo.size()) { + if (index < 0 || index >= m_rows.size()) { return nullptr; } - return m_tinfo[index]; + return m_rows[index]; } -void TransactionHistory::refresh(quint32 accountIndex) +void TransactionHistory::refresh() { QDateTime firstDateTime = QDate(2014, 4, 18).startOfDay(); QDateTime lastDateTime = QDateTime::currentDateTime().addDays(1); // tomorrow (guard against jitter and timezones) @@ -53,55 +54,214 @@ void TransactionHistory::refresh(quint32 accountIndex) { QWriteLocker locker(&m_lock); - qDeleteAll(m_tinfo); - m_tinfo.clear(); + clearRows(); quint64 lastTxHeight = 0; m_locked = false; m_minutesToUnlock = 0; - m_pimpl->refresh(); - for (const auto i : m_pimpl->getAll()) { - if (i->subaddrAccount() != accountIndex) { + + uint64_t min_height = 0; + uint64_t max_height = (uint64_t)-1; + uint64_t wallet_height = m_wallet->blockChainHeight(); + uint32_t account = m_wallet->currentSubaddressAccount(); + + // transactions are stored in wallet2: + // - confirmed_transfer_details - out transfers + // - unconfirmed_transfer_details - pending out transfers + // - payment_details - input transfers + + // payments are "input transactions"; + // one input transaction contains only one transfer. e.g. - <100XMR> + + std::list> in_payments; + m_wallet2->get_payments(in_payments, min_height, max_height); + for (std::list>::const_iterator i = in_payments.begin(); i != in_payments.end(); ++i) + { + const tools::wallet2::payment_details &pd = i->second; + if (pd.m_subaddr_index.major != account) { continue; } - m_tinfo.append(new TransactionInfo(i, this)); + std::string payment_id = epee::string_tools::pod_to_hex(i->first); + if (payment_id.substr(16).find_first_not_of('0') == std::string::npos) + payment_id = payment_id.substr(0,16); + + auto* t = new TransactionRow(); + t->m_paymentId = QString::fromStdString(payment_id); + t->m_coinbase = pd.m_coinbase; + t->m_amount = pd.m_amount; + t->m_fee = pd.m_fee; + t->m_direction = TransactionRow::Direction_In; + t->m_hash = QString::fromStdString(epee::string_tools::pod_to_hex(pd.m_tx_hash)); + t->m_blockHeight = pd.m_block_height; + t->m_description = QString::fromStdString(m_wallet2->get_tx_note(pd.m_tx_hash)); + t->m_subaddrIndex = { pd.m_subaddr_index.minor }; + t->m_subaddrAccount = pd.m_subaddr_index.major; + t->m_label = QString::fromStdString(m_wallet2->get_subaddress_label(pd.m_subaddr_index)); + t->m_timestamp = QDateTime::fromSecsSinceEpoch(pd.m_timestamp); + t->m_confirmations = (wallet_height > pd.m_block_height) ? wallet_height - pd.m_block_height : 0; + t->m_unlockTime = pd.m_unlock_time; + + m_rows.append(t); + } + + // confirmed output transactions + // one output transaction may contain more than one money transfer, e.g. + // : + // transfer1: 100XMR to + // transfer2: 50XMR to + // fee: fee charged per transaction + // - const TransactionInfo *ti = m_tinfo.back(); - // looking for transactions timestamp scope - if (ti->timestamp() >= lastDateTime) { - lastDateTime = ti->timestamp(); + std::list> out_payments; + m_wallet2->get_payments_out(out_payments, min_height, max_height); + + for (std::list>::const_iterator i = out_payments.begin(); + i != out_payments.end(); ++i) { + + const crypto::hash &hash = i->first; + const tools::wallet2::confirmed_transfer_details &pd = i->second; + if (pd.m_subaddr_account != account) { + continue; } - if (ti->timestamp() <= firstDateTime) { - firstDateTime = ti->timestamp(); + + uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known + uint64_t fee = pd.m_amount_in - pd.m_amount_out; + + + std::string payment_id = epee::string_tools::pod_to_hex(i->second.m_payment_id); + if (payment_id.substr(16).find_first_not_of('0') == std::string::npos) + payment_id = payment_id.substr(0,16); + + + auto* t = new TransactionRow(); + t->m_paymentId = QString::fromStdString(payment_id); + t->m_amount = pd.m_amount_in - change - fee; + t->m_fee = fee; + t->m_direction = TransactionRow::Direction_Out; + t->m_hash = QString::fromStdString(epee::string_tools::pod_to_hex(hash)); + t->m_blockHeight = pd.m_block_height; + t->m_description = QString::fromStdString(m_wallet2->get_tx_note(hash)); + t->m_subaddrAccount = pd.m_subaddr_account; + t->m_label = QString::fromStdString(pd.m_subaddr_indices.size() == 1 ? m_wallet2->get_subaddress_label({pd.m_subaddr_account, *pd.m_subaddr_indices.begin()}) : ""); + t->m_timestamp = QDateTime::fromSecsSinceEpoch(pd.m_timestamp); + t->m_confirmations = (wallet_height > pd.m_block_height) ? wallet_height - pd.m_block_height : 0; + + for (uint32_t idx : t->subaddrIndex()) + { + t->m_subaddrIndex.insert(idx); + } + + // single output transaction might contain multiple transfers + for (auto const &d: pd.m_dests) + { + Transfer *transfer = new Transfer(d.amount, QString::fromStdString(d.address(m_wallet2->nettype(), pd.m_payment_id)), this); + t->m_transfers.append(transfer); } - quint64 requiredConfirmations = (ti->blockHeight() < ti->unlockTime()) ? ti->unlockTime() - ti->blockHeight() : 10; - // store last tx height - if (ti->confirmations() < requiredConfirmations && ti->blockHeight() >= lastTxHeight) { - lastTxHeight = ti->blockHeight(); - // TODO: Fetch block time and confirmations needed from wallet2? - m_minutesToUnlock = (requiredConfirmations - ti->confirmations()) * 2; - m_locked = true; + for (auto const &r: pd.m_rings) + { + Ring *ring = new Ring(QString::fromStdString(epee::string_tools::pod_to_hex(r.first)), cryptonote::relative_output_offsets_to_absolute(r.second), this); + t->m_rings.append(ring); } + + m_rows.append(t); } - } - emit refreshFinished(); + // unconfirmed output transactions + std::list> upayments_out; + m_wallet2->get_unconfirmed_payments_out(upayments_out); + for (std::list>::const_iterator i = upayments_out.begin(); i != upayments_out.end(); ++i) { + const tools::wallet2::unconfirmed_transfer_details &pd = i->second; + if (pd.m_subaddr_account != account) { + continue; + } - if (m_firstDateTime != firstDateTime) { - m_firstDateTime = firstDateTime; - emit firstDateTimeChanged(); - } - if (m_lastDateTime != lastDateTime) { - m_lastDateTime = lastDateTime; - emit lastDateTimeChanged(); + const crypto::hash &hash = i->first; + uint64_t amount = pd.m_amount_in; + uint64_t fee = amount - pd.m_amount_out; + std::string payment_id = epee::string_tools::pod_to_hex(i->second.m_payment_id); + if (payment_id.substr(16).find_first_not_of('0') == std::string::npos) + payment_id = payment_id.substr(0,16); + bool is_failed = pd.m_state == tools::wallet2::unconfirmed_transfer_details::failed; + + auto *t = new TransactionRow(); + t->m_paymentId = QString::fromStdString(payment_id); + t->m_amount = amount - pd.m_change - fee; + t->m_fee = fee; + t->m_direction = TransactionRow::Direction_Out; + t->m_failed = is_failed; + t->m_pending = true; + t->m_hash = QString::fromStdString(epee::string_tools::pod_to_hex(hash)); + t->m_description = QString::fromStdString(m_wallet2->get_tx_note(hash)); + t->m_subaddrAccount = pd.m_subaddr_account; + t->m_label = QString::fromStdString(pd.m_subaddr_indices.size() == 1 ? m_wallet2->get_subaddress_label({pd.m_subaddr_account, *pd.m_subaddr_indices.begin()}) : ""); + t->m_timestamp = QDateTime::fromSecsSinceEpoch(pd.m_timestamp); + t->m_confirmations = 0; + for (uint32_t idx : t->subaddrIndex()) + { + t->m_subaddrIndex.insert(idx); + } + + for (auto const &d: pd.m_dests) + { + Transfer *transfer = new Transfer(d.amount, QString::fromStdString(d.address(m_wallet2->nettype(), pd.m_payment_id)), this); + t->m_transfers.append(transfer); + } + for (auto const &r: pd.m_rings) + { + Ring *ring = new Ring(QString::fromStdString(epee::string_tools::pod_to_hex(r.first)), cryptonote::relative_output_offsets_to_absolute(r.second), this); + t->m_rings.append(ring); + } + + m_rows.append(t); + } + + + // unconfirmed payments (tx pool) + std::list> upayments; + m_wallet2->get_unconfirmed_payments(upayments); + for (std::list>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) { + const tools::wallet2::payment_details &pd = i->second.m_pd; + if (pd.m_subaddr_index.major != account) { + continue; + } + + std::string payment_id = epee::string_tools::pod_to_hex(i->first); + if (payment_id.substr(16).find_first_not_of('0') == std::string::npos) + payment_id = payment_id.substr(0,16); + auto *t = new TransactionRow(); + t->m_paymentId = QString::fromStdString(payment_id); + t->m_amount = pd.m_amount; + t->m_direction = TransactionRow::Direction_In; + t->m_hash = QString::fromStdString(epee::string_tools::pod_to_hex(pd.m_tx_hash)); + t->m_blockHeight = pd.m_block_height; + t->m_description = QString::fromStdString(m_wallet2->get_tx_note(pd.m_tx_hash)); + t->m_pending = true; + t->m_subaddrIndex = { pd.m_subaddr_index.minor }; + t->m_subaddrAccount = pd.m_subaddr_index.major; + t->m_label = QString::fromStdString(m_wallet2->get_subaddress_label(pd.m_subaddr_index)); + t->m_timestamp = QDateTime::fromSecsSinceEpoch(pd.m_timestamp); + t->m_confirmations = 0; + + m_rows.append(t); + + LOG_PRINT_L1(__FUNCTION__ << ": Unconfirmed payment found " << pd.m_amount); + } } + + emit refreshFinished(); } void TransactionHistory::setTxNote(const QString &txid, const QString ¬e) { - m_pimpl->setTxNote(txid.toStdString(), note.toStdString()); + cryptonote::blobdata txid_data; + if(!epee::string_tools::parse_hexstr_to_binbuff(txid.toStdString(), txid_data) || txid_data.size() != sizeof(crypto::hash)) + return; + const crypto::hash htxid = *reinterpret_cast(txid_data.data()); + + m_wallet2->set_tx_note(htxid, note.toStdString()); + refresh(); emit txNoteChanged(); } @@ -109,7 +269,7 @@ quint64 TransactionHistory::count() const { QReadLocker locker(&m_lock); - return m_tinfo.count(); + return m_rows.length(); } QDateTime TransactionHistory::firstDateTime() const @@ -133,8 +293,12 @@ bool TransactionHistory::TransactionHistory::locked() const } -TransactionHistory::TransactionHistory(Monero::TransactionHistory *pimpl, QObject *parent) - : QObject(parent), m_pimpl(pimpl), m_minutesToUnlock(0), m_locked(false) +TransactionHistory::TransactionHistory(Wallet *wallet, tools::wallet2 *wallet2, QObject *parent) + : QObject(parent) + , m_wallet(wallet) + , m_wallet2(wallet2) + , m_minutesToUnlock(0) + , m_locked(false) { #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) m_firstDateTime = QDate(2014, 4, 18).startOfDay(); @@ -144,22 +308,25 @@ TransactionHistory::TransactionHistory(Monero::TransactionHistory *pimpl, QObjec m_lastDateTime = QDateTime::currentDateTime().addDays(1); // tomorrow (guard against jitter and timezones) } +void TransactionHistory::clearRows() { + qDeleteAll(m_rows); + m_rows.clear(); +} + bool TransactionHistory::writeCSV(const QString &path) { QString data; QReadLocker locker(&m_lock); - auto transactions = m_pimpl->getAll(); + auto transactions = m_rows; - std::sort(transactions.begin(), transactions.end(), [](const Monero::TransactionInfo *info1, const Monero::TransactionInfo *info2){ + std::sort(m_rows.begin(), m_rows.end(), [](const TransactionRow *info1, const TransactionRow *info2){ return info1->blockHeight() < info2->blockHeight(); }); - for (const auto &tx : transactions) { - TransactionInfo info(tx, this); - + for (const auto &info : transactions) { // collect column data - QDateTime timeStamp = info.timestamp(); - double amount = info.amount(); + QDateTime timeStamp = info->timestamp(); + double amount = info->amount(); // calc historical fiat price QString fiatAmount; @@ -176,31 +343,31 @@ bool TransactionHistory::writeCSV(const QString &path) { fiatAmount = "\"?\""; QString direction = QString(""); - if (info.direction() == TransactionInfo::Direction_In) + if (info->direction() == TransactionRow::Direction_In) direction = QString("in"); - else if (info.direction() == TransactionInfo::Direction_Out) + else if (info->direction() == TransactionRow::Direction_Out) direction = QString("out"); else continue; // skip TransactionInfo::Direction_Both - QString displayAmount = info.displayAmount(); - QString paymentId = info.paymentId(); + QString displayAmount = info->displayAmount(); + QString paymentId = info->paymentId(); if (paymentId == "0000000000000000") { paymentId = ""; } - QString date = QString("%1T%2Z").arg(info.date(), info.time()); // ISO 8601 + QString date = QString("%1T%2Z").arg(info->date(), info->time()); // ISO 8601 - QString balanceDelta = WalletManager::displayAmount(info.balanceDelta()); - if (info.direction() == TransactionInfo::Direction_Out) { + QString balanceDelta = WalletManager::displayAmount(info->balanceDelta()); + if (info->direction() == TransactionRow::Direction_Out) { balanceDelta = "-" + balanceDelta; } // format and write QString line = QString("\n%1,%2,\"%3\",%4,\"%5\",%6,%7,%8,\"%9\",\"%10\",\"%11\",%12,\"%13\"") - .arg(QString::number(info.blockHeight()), QString::number(timeStamp.toSecsSinceEpoch()), date, - QString::number(info.subaddrAccount()), direction, balanceDelta, info.displayAmount(), - info.fee(), info.hash(), info.description(), paymentId, fiatAmount, preferredFiatSymbol); + .arg(QString::number(info->blockHeight()), QString::number(timeStamp.toSecsSinceEpoch()), date, + QString::number(info->subaddrAccount()), direction, balanceDelta, info->displayAmount(), + info->fee(), info->hash(), info->description(), paymentId, fiatAmount, preferredFiatSymbol); data += line; } diff --git a/src/libwalletqt/TransactionHistory.h b/src/libwalletqt/TransactionHistory.h index 1ee3c147..c55298d5 100644 --- a/src/libwalletqt/TransactionHistory.h +++ b/src/libwalletqt/TransactionHistory.h @@ -11,6 +11,10 @@ #include #include +#include "rows/TransactionRow.h" +#include "Wallet.h" +#include "wallet/wallet2.h" + namespace Monero { struct TransactionHistory; } @@ -20,24 +24,20 @@ class TransactionInfo; class TransactionHistory : public QObject { Q_OBJECT - Q_PROPERTY(int count READ count) - Q_PROPERTY(QDateTime firstDateTime READ firstDateTime NOTIFY firstDateTimeChanged) - Q_PROPERTY(QDateTime lastDateTime READ lastDateTime NOTIFY lastDateTimeChanged) - Q_PROPERTY(int minutesToUnlock READ minutesToUnlock) - Q_PROPERTY(bool locked READ locked) public: - Q_INVOKABLE bool transaction(int index, std::function callback); - Q_INVOKABLE TransactionInfo * transaction(const QString &id); - TransactionInfo* transaction(int index); - Q_INVOKABLE void refresh(quint32 accountIndex); - Q_INVOKABLE void setTxNote(const QString &txid, const QString ¬e); - Q_INVOKABLE bool writeCSV(const QString &path); + bool transaction(int index, std::function callback); + TransactionRow * transaction(const QString &id); + TransactionRow* transaction(int index); + void refresh(); + void setTxNote(const QString &txid, const QString ¬e); + bool writeCSV(const QString &path); quint64 count() const; QDateTime firstDateTime() const; QDateTime lastDateTime() const; quint64 minutesToUnlock() const; bool locked() const; + void clearRows(); signals: void refreshStarted() const; @@ -47,13 +47,16 @@ signals: void txNoteChanged() const; private: - explicit TransactionHistory(Monero::TransactionHistory * pimpl, QObject *parent = nullptr); + explicit TransactionHistory(Wallet *wallet, tools::wallet2 *wallet2, QObject *parent = nullptr); private: friend class Wallet; mutable QReadWriteLock m_lock; - Monero::TransactionHistory * m_pimpl; - mutable QList m_tinfo; + + Wallet *m_wallet; + tools::wallet2 *m_wallet2; + QList m_rows; + mutable QDateTime m_firstDateTime; mutable QDateTime m_lastDateTime; mutable int m_minutesToUnlock; diff --git a/src/libwalletqt/TransactionInfo.cpp b/src/libwalletqt/TransactionInfo.cpp deleted file mode 100644 index fe78e916..00000000 --- a/src/libwalletqt/TransactionInfo.cpp +++ /dev/null @@ -1,202 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// SPDX-FileCopyrightText: 2020-2023 The Monero Project - -#include "TransactionInfo.h" -#include "libwalletqt/WalletManager.h" -#include "Transfer.h" -#include "Ring.h" - -TransactionInfo::Direction TransactionInfo::direction() const -{ - return m_direction; -} - -bool TransactionInfo::isPending() const -{ - return m_pending; -} - -bool TransactionInfo::isFailed() const -{ - return m_failed; -} - -bool TransactionInfo::isCoinbase() const -{ - return m_coinbase; -} - -quint64 TransactionInfo::balanceDelta() const -{ - if (m_direction == Direction_In) { - return m_amount; - } - else if (m_direction == Direction_Out) { - return m_amount + m_fee; - } - return m_amount; -} - -double TransactionInfo::amount() const -{ - // there's no unsigned uint64 for JS, so better use double - return displayAmount().toDouble(); -} - -quint64 TransactionInfo::atomicAmount() const -{ - return m_amount; -} - -QString TransactionInfo::displayAmount() const -{ - return WalletManager::displayAmount(m_amount); -} - -quint64 TransactionInfo::atomicFee() const -{ - return m_fee; -} - -QString TransactionInfo::fee() const -{ - if(m_fee == 0) - return ""; - return WalletManager::displayAmount(m_fee); -} - -quint64 TransactionInfo::blockHeight() const -{ - return m_blockHeight; -} - -QString TransactionInfo::description() const -{ - return m_description; -} - -QSet TransactionInfo::subaddrIndex() const -{ - return m_subaddrIndex; -} - -quint32 TransactionInfo::subaddrAccount() const -{ - return m_subaddrAccount; -} - -QString TransactionInfo::label() const -{ - return m_label; -} - -quint64 TransactionInfo::confirmations() const -{ - return m_confirmations; -} - -quint64 TransactionInfo::confirmationsRequired() const -{ - return (m_blockHeight < m_unlockTime) ? m_unlockTime - m_blockHeight : 10; -} - -quint64 TransactionInfo::unlockTime() const -{ - return m_unlockTime; -} - -QString TransactionInfo::hash() const -{ - return m_hash; -} - -QDateTime TransactionInfo::timestamp() const -{ - return m_timestamp; -} - -QString TransactionInfo::date() const -{ - return timestamp().date().toString(Qt::ISODate); -} - -QString TransactionInfo::time() const -{ - return timestamp().time().toString(Qt::ISODate); -} - -QString TransactionInfo::paymentId() const -{ - return m_paymentId; -} - -QString TransactionInfo::destinations_formatted() const -{ - QString destinations; - for (auto const& t: m_transfers) { - if (!destinations.isEmpty()) - destinations += "
"; - destinations += WalletManager::displayAmount(t->amount()) + ": " + t->address(); - } - return destinations; -} - -QList TransactionInfo::destinations() const -{ - QList dests; - for (auto const& t: m_transfers) { - dests.append(t->address()); - } - return dests; -} - -QList TransactionInfo::transfers() const { - return m_transfers; -} - -QString TransactionInfo::rings_formatted() const -{ - QString rings; - for (auto const& r: m_rings) { - rings += r->keyImage() + ": \n"; - for (uint64_t m : r->ringMembers()){ - rings += QString::number(m) + " "; - } - rings += "\n\n"; - } - return rings; -} - -TransactionInfo::TransactionInfo(const Monero::TransactionInfo *pimpl, QObject *parent) - : QObject(parent) - , m_amount(pimpl->amount()) - , m_blockHeight(pimpl->blockHeight()) - , m_description(QString::fromStdString(pimpl->description())) - , m_confirmations(pimpl->confirmations()) - , m_direction(static_cast(pimpl->direction())) - , m_failed(pimpl->isFailed()) - , m_fee(pimpl->fee()) - , m_hash(QString::fromStdString(pimpl->hash())) - , m_label(QString::fromStdString(pimpl->label())) - , m_paymentId(QString::fromStdString(pimpl->paymentId())) - , m_pending(pimpl->isPending()) - , m_subaddrAccount(pimpl->subaddrAccount()) - , m_timestamp(QDateTime::fromSecsSinceEpoch(pimpl->timestamp())) - , m_unlockTime(pimpl->unlockTime()) - , m_coinbase(pimpl->isCoinbase()) -{ - for (auto const &t: pimpl->transfers()) - { - Transfer *transfer = new Transfer(t.amount, QString::fromStdString(t.address), this); - m_transfers.append(transfer); - } - for (auto const &r: pimpl->rings()) - { - Ring *ring = new Ring(QString::fromStdString(r.first), r.second, this); - m_rings.append(ring); - } - for (uint32_t i : pimpl->subaddrIndex()) - { - m_subaddrIndex.insert(i); - } -} diff --git a/src/libwalletqt/Transfer.h b/src/libwalletqt/Transfer.h index f3d961ba..8c18609c 100644 --- a/src/libwalletqt/Transfer.h +++ b/src/libwalletqt/Transfer.h @@ -12,7 +12,7 @@ class Transfer : public QObject { Q_OBJECT -private: +public: explicit Transfer(uint64_t _amount, QString _address, QObject *parent = 0) : QObject(parent), m_amount(_amount), m_address(std::move(_address)) {}; private: diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp index 9b6117d6..87f5e9d2 100644 --- a/src/libwalletqt/Wallet.cpp +++ b/src/libwalletqt/Wallet.cpp @@ -32,21 +32,21 @@ namespace { Wallet::Wallet(Monero::Wallet *wallet, QObject *parent) : QObject(parent) , m_walletImpl(wallet) - , m_history(new TransactionHistory(m_walletImpl->history(), this)) + , m_history(new TransactionHistory(this, wallet->getWallet(), this)) , m_historyModel(nullptr) - , m_addressBook(new AddressBook(m_walletImpl->addressBook(), this)) + , m_addressBook(new AddressBook(this, wallet->getWallet(), this)) , m_addressBookModel(nullptr) , m_daemonBlockChainHeight(0) , m_daemonBlockChainTargetHeight(0) , m_connectionStatus(Wallet::ConnectionStatus_Disconnected) , m_currentSubaddressAccount(0) , m_subaddress(new Subaddress(this, wallet->getWallet(), this)) - , m_subaddressAccount(new SubaddressAccount(m_walletImpl->subaddressAccount(), this)) + , m_subaddressAccount(new SubaddressAccount(this, wallet->getWallet(), this)) , m_refreshNow(false) , m_refreshEnabled(false) , m_scheduler(this) , m_useSSL(true) - , m_coins(new Coins(m_walletImpl->coins(), this)) + , m_coins(new Coins(this, wallet->getWallet(), this)) , m_storeTimer(new QTimer(this)) { m_walletListener = new WalletListenerImpl(this); @@ -71,7 +71,7 @@ Wallet::Wallet(Monero::Wallet *wallet, QObject *parent) } connect(this->history(), &TransactionHistory::txNoteChanged, [this]{ - this->history()->refresh(this->currentSubaddressAccount()); + this->history()->refresh(); }); connect(this, &Wallet::refreshed, this, &Wallet::onRefreshed); @@ -185,8 +185,8 @@ void Wallet::switchSubaddressAccount(quint32 accountIndex) { qWarning() << "failed to set " << ATTRIBUTE_SUBADDRESS_ACCOUNT << " cache attribute"; } m_subaddress->refresh(m_currentSubaddressAccount); - m_history->refresh(m_currentSubaddressAccount); - m_coins->refresh(m_currentSubaddressAccount); + m_history->refresh(); + m_coins->refresh(); this->subaddressModel()->setCurrentSubaddressAccount(m_currentSubaddressAccount); this->coinsModel()->setCurrentSubaddressAccount(m_currentSubaddressAccount); this->updateBalance(); @@ -453,8 +453,8 @@ void Wallet::onRefreshed(bool success, const QString &message) { } void Wallet::refreshModels() { - m_history->refresh(this->currentSubaddressAccount()); - m_coins->refresh(this->currentSubaddressAccount()); + m_history->refresh(); + m_coins->refresh(); bool r = this->subaddress()->refresh(this->currentSubaddressAccount()); if (!r) { @@ -776,8 +776,8 @@ void Wallet::onTransactionCommitted(bool success, PendingTransaction *tx, const // 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->history()->refresh(); + this->coins()->refresh(); this->updateBalance(); if (!success) { diff --git a/src/libwalletqt/rows/AccountRow.cpp b/src/libwalletqt/rows/AccountRow.cpp new file mode 100644 index 00000000..591402a5 --- /dev/null +++ b/src/libwalletqt/rows/AccountRow.cpp @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: 2020-2023 The Monero Project + +#include "AccountRow.h" +#include "WalletManager.h" + +qsizetype AccountRow::getRow() const { + return m_row; +} + +const QString& AccountRow::getAddress() const { + return m_address; +} + +const QString& AccountRow::getLabel() const { + return m_label; +} + +QString AccountRow::getBalance() const { + return WalletManager::displayAmount(m_balance); +} + +QString AccountRow::getUnlockedBalance() const { + return WalletManager::displayAmount(m_unlockedBalance); +} \ No newline at end of file diff --git a/src/libwalletqt/rows/AccountRow.h b/src/libwalletqt/rows/AccountRow.h new file mode 100644 index 00000000..46283fdf --- /dev/null +++ b/src/libwalletqt/rows/AccountRow.h @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: 2020-2023 The Monero Project + +#ifndef FEATHER_ACCOUNTROW_H +#define FEATHER_ACCOUNTROW_H + +#include + +class AccountRow : public QObject +{ +Q_OBJECT + +public: + AccountRow(QObject *parent, qsizetype row, const QString& address, const QString &label, uint64_t balance, uint64_t unlockedBalance) + : QObject(parent) + , m_row(row) + , m_address(address) + , m_label(label) + , m_balance(balance) + , m_unlockedBalance(unlockedBalance) {} + + qsizetype getRow() const; + const QString& getAddress() const; + const QString& getLabel() const; + QString getBalance() const; + QString getUnlockedBalance() const; + +private: + qsizetype m_row; + QString m_address; + QString m_label; + uint64_t m_balance; + uint64_t m_unlockedBalance; +}; + +#endif //FEATHER_ACCOUNTROW_H diff --git a/src/libwalletqt/CoinsInfo.cpp b/src/libwalletqt/rows/CoinsInfo.cpp similarity index 60% rename from src/libwalletqt/CoinsInfo.cpp rename to src/libwalletqt/rows/CoinsInfo.cpp index 925d6c6f..2c003648 100644 --- a/src/libwalletqt/CoinsInfo.cpp +++ b/src/libwalletqt/rows/CoinsInfo.cpp @@ -74,7 +74,15 @@ QString CoinsInfo::address() const { QString CoinsInfo::addressLabel() const { if (m_subaddrIndex == 0) { - return m_coinbase ? "Coinbase" : "Change"; + if (m_coinbase) { + return "Coinbase"; + } + if (m_change) { + return "Change"; + } + if (m_addressLabel == "Primary account") { + return "Primary address"; + } } return m_addressLabel; @@ -108,29 +116,28 @@ QString CoinsInfo::description() const { return m_description; } -CoinsInfo::CoinsInfo(const Monero::CoinsInfo *pimpl, QObject *parent) +bool CoinsInfo::change() const { + return m_change; +} + +CoinsInfo::CoinsInfo(QObject *parent) : QObject(parent) - , m_blockHeight(pimpl->blockHeight()) - , m_hash(QString::fromStdString(pimpl->hash())) - , m_internalOutputIndex(pimpl->internalOutputIndex()) - , m_globalOutputIndex(pimpl->globalOutputIndex()) - , m_spent(pimpl->spent()) - , m_frozen(pimpl->frozen()) - , m_spentHeight(pimpl->spentHeight()) - , m_amount(pimpl->amount()) - , m_rct(pimpl->rct()) - , m_keyImageKnown(pimpl->keyImageKnown()) - , m_pkIndex(pimpl->pkIndex()) - , m_subaddrIndex(pimpl->subaddrIndex()) - , m_subaddrAccount(pimpl->subaddrAccount()) - , m_address(QString::fromStdString(pimpl->address())) - , m_addressLabel(QString::fromStdString(pimpl->addressLabel())) - , m_keyImage(QString::fromStdString(pimpl->keyImage())) - , m_unlockTime(pimpl->unlockTime()) - , m_unlocked(pimpl->unlocked()) - , m_pubKey(QString::fromStdString(pimpl->pubKey())) - , m_coinbase(pimpl->coinbase()) - , m_description(QString::fromStdString(pimpl->description())) + , m_blockHeight(0) + , m_internalOutputIndex(0) + , m_globalOutputIndex(0) + , m_spent(false) + , m_frozen(false) + , m_spentHeight(0) + , m_amount(0) + , m_rct(false) + , m_keyImageKnown(false) + , m_pkIndex(0) + , m_subaddrIndex(0) + , m_subaddrAccount(0) + , m_unlockTime(0) + , m_unlocked(false) + , m_coinbase(false) + , m_change(false) { } diff --git a/src/libwalletqt/CoinsInfo.h b/src/libwalletqt/rows/CoinsInfo.h similarity index 58% rename from src/libwalletqt/CoinsInfo.h rename to src/libwalletqt/rows/CoinsInfo.h index 42c39db3..a68fd1bc 100644 --- a/src/libwalletqt/CoinsInfo.h +++ b/src/libwalletqt/rows/CoinsInfo.h @@ -14,28 +14,6 @@ class Coins; class CoinsInfo : public QObject { Q_OBJECT - Q_PROPERTY(quint64 blockHeight READ blockHeight) - Q_PROPERTY(QString hash READ hash) - Q_PROPERTY(quint64 internalOutputIndex READ internalOutputIndex) - Q_PROPERTY(quint64 globalOutputIndex READ globalOutputIndex) - Q_PROPERTY(bool spent READ spent) - Q_PROPERTY(bool frozen READ frozen) - Q_PROPERTY(quint64 spentHeight READ spentHeight) - Q_PROPERTY(quint64 amount READ amount) - Q_PROPERTY(QString displayAmount READ displayAmount) - Q_PROPERTY(bool rct READ rct) - Q_PROPERTY(bool keyImageKnown READ keyImageKnown) - Q_PROPERTY(quint64 pkIndex READ pkIndex) - Q_PROPERTY(quint32 subaddrIndex READ subaddrIndex) - Q_PROPERTY(quint32 subaddrAccount READ subaddrAccount) - Q_PROPERTY(QString address READ address) - Q_PROPERTY(QString addressLabel READ addressLabel) - Q_PROPERTY(QString keyImage READ keyImage) - Q_PROPERTY(quint64 unlockTime READ unlockTime) - Q_PROPERTY(bool unlocked READ unlocked) - Q_PROPERTY(QString pubKey READ pubKey) - Q_PROPERTY(bool coinbase READ coinbase) - Q_PROPERTY(QString description READ description) public: quint64 blockHeight() const; @@ -60,11 +38,13 @@ public: QString pubKey() const; bool coinbase() const; QString description() const; + bool change() const; void setUnlocked(bool unlocked); private: - explicit CoinsInfo(const Monero::CoinsInfo *pimpl, QObject *parent = nullptr); + explicit CoinsInfo(QObject *parent); + private: friend class Coins; @@ -89,6 +69,7 @@ private: QString m_pubKey; bool m_coinbase; QString m_description; + bool m_change; }; #endif //FEATHER_COINSINFO_H diff --git a/src/libwalletqt/rows/ContactRow.cpp b/src/libwalletqt/rows/ContactRow.cpp new file mode 100644 index 00000000..e31957a3 --- /dev/null +++ b/src/libwalletqt/rows/ContactRow.cpp @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: 2020-2023 The Monero Project + +#include "ContactRow.h" + +qsizetype ContactRow::getRow() const { + return m_row; +} + +const QString& ContactRow::getAddress() const { + return m_address; +} + +const QString& ContactRow::getLabel() const { + return m_label; +} \ No newline at end of file diff --git a/src/libwalletqt/rows/ContactRow.h b/src/libwalletqt/rows/ContactRow.h new file mode 100644 index 00000000..901017ad --- /dev/null +++ b/src/libwalletqt/rows/ContactRow.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: 2020-2023 The Monero Project + +#ifndef FEATHER_CONTACTROW_H +#define FEATHER_CONTACTROW_H + +#include + +class ContactRow : public QObject +{ + Q_OBJECT + +public: + ContactRow(QObject *parent, qsizetype row, const QString& address, const QString &label) + : QObject(parent) + , m_row(row) + , m_address(address) + , m_label(label) {} + + qsizetype getRow() const; + const QString& getAddress() const; + const QString& getLabel() const; + +private: + qsizetype m_row; + QString m_address; + QString m_label; +}; + + +#endif //FEATHER_CONTACTROW_H diff --git a/src/libwalletqt/rows/TransactionRow.cpp b/src/libwalletqt/rows/TransactionRow.cpp new file mode 100644 index 00000000..e2f74654 --- /dev/null +++ b/src/libwalletqt/rows/TransactionRow.cpp @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: 2020-2023 The Monero Project + +#include "TransactionRow.h" +#include "WalletManager.h" +#include "Transfer.h" +#include "Ring.h" + +TransactionRow::TransactionRow() + : m_direction(TransactionRow::Direction_Out) + , m_pending(false) + , m_failed(false) + , m_coinbase(false) + , m_amount(0) + , m_fee(0) + , m_blockHeight(0) + , m_subaddrAccount(0) + , m_confirmations(0) + , m_unlockTime(0) + , m_confirmationsRequired(0) +{ +} + +TransactionRow::Direction TransactionRow::direction() const +{ + return m_direction; +} + +bool TransactionRow::isPending() const +{ + return m_pending; +} + +bool TransactionRow::isFailed() const +{ + return m_failed; +} + +bool TransactionRow::isCoinbase() const +{ + return m_coinbase; +} + +quint64 TransactionRow::balanceDelta() const +{ + if (m_direction == Direction_In) { + return m_amount; + } + else if (m_direction == Direction_Out) { + return m_amount + m_fee; + } + return m_amount; +} + +double TransactionRow::amount() const +{ + // there's no unsigned uint64 for JS, so better use double + return displayAmount().toDouble(); +} + +quint64 TransactionRow::atomicAmount() const +{ + return m_amount; +} + +QString TransactionRow::displayAmount() const +{ + return WalletManager::displayAmount(m_amount); +} + +quint64 TransactionRow::atomicFee() const +{ + return m_fee; +} + +QString TransactionRow::fee() const +{ + if(m_fee == 0) + return ""; + return WalletManager::displayAmount(m_fee); +} + +quint64 TransactionRow::blockHeight() const +{ + return m_blockHeight; +} + +QString TransactionRow::description() const +{ + return m_description; +} + +QSet TransactionRow::subaddrIndex() const +{ + return m_subaddrIndex; +} + +quint32 TransactionRow::subaddrAccount() const +{ + return m_subaddrAccount; +} + +QString TransactionRow::label() const +{ + return m_label; +} + +quint64 TransactionRow::confirmations() const +{ + return m_confirmations; +} + +quint64 TransactionRow::confirmationsRequired() const +{ + return (m_blockHeight < m_unlockTime) ? m_unlockTime - m_blockHeight : 10; +} + +quint64 TransactionRow::unlockTime() const +{ + return m_unlockTime; +} + +QString TransactionRow::hash() const +{ + return m_hash; +} + +QDateTime TransactionRow::timestamp() const +{ + return m_timestamp; +} + +QString TransactionRow::date() const +{ + return timestamp().date().toString(Qt::ISODate); +} + +QString TransactionRow::time() const +{ + return timestamp().time().toString(Qt::ISODate); +} + +QString TransactionRow::paymentId() const +{ + return m_paymentId; +} + +QString TransactionRow::destinations_formatted() const +{ + QString destinations; + for (auto const& t: m_transfers) { + if (!destinations.isEmpty()) + destinations += "
"; + destinations += WalletManager::displayAmount(t->amount()) + ": " + t->address(); + } + return destinations; +} + +QList TransactionRow::destinations() const +{ + QList dests; + for (auto const& t: m_transfers) { + dests.append(t->address()); + } + return dests; +} + +QList TransactionRow::transfers() const { + return m_transfers; +} + +QString TransactionRow::rings_formatted() const +{ + QString rings; + for (auto const& r: m_rings) { + rings += r->keyImage() + ": \n"; + for (uint64_t m : r->ringMembers()){ + rings += QString::number(m) + " "; + } + rings += "\n\n"; + } + return rings; +} \ No newline at end of file diff --git a/src/libwalletqt/TransactionInfo.h b/src/libwalletqt/rows/TransactionRow.h similarity index 54% rename from src/libwalletqt/TransactionInfo.h rename to src/libwalletqt/rows/TransactionRow.h index 9e496c4d..06030f6e 100644 --- a/src/libwalletqt/TransactionInfo.h +++ b/src/libwalletqt/rows/TransactionRow.h @@ -1,48 +1,24 @@ // SPDX-License-Identifier: BSD-3-Clause // SPDX-FileCopyrightText: 2020-2023 The Monero Project -#ifndef TRANSACTIONINFO_H -#define TRANSACTIONINFO_H - -#include -#include -#include -#include +#ifndef FEATHER_TRANSACTIONROW_H +#define FEATHER_TRANSACTIONROW_H class Transfer; class Ring; -class TransactionInfo : public QObject +#include +#include +#include + +class TransactionRow : public QObject { Q_OBJECT - Q_PROPERTY(Direction direction READ direction) - Q_PROPERTY(bool isPending READ isPending) - Q_PROPERTY(bool isFailed READ isFailed) - Q_PROPERTY(bool isCoinbase READ isCoinbase) - Q_PROPERTY(double amount READ amount) - Q_PROPERTY(quint64 atomicAmount READ atomicAmount) - Q_PROPERTY(QString displayAmount READ displayAmount) - Q_PROPERTY(QString fee READ fee) - Q_PROPERTY(quint64 blockHeight READ blockHeight) - Q_PROPERTY(QString description READ description) - Q_PROPERTY(QSet subaddrIndex READ subaddrIndex) - Q_PROPERTY(quint32 subaddrAccount READ subaddrAccount) - Q_PROPERTY(QString label READ label) - Q_PROPERTY(quint64 confirmations READ confirmations) - Q_PROPERTY(quint64 confirmationsRequired READ confirmationsRequired) - Q_PROPERTY(quint64 unlockTime READ unlockTime) - Q_PROPERTY(QString hash READ hash) - Q_PROPERTY(QDateTime timestamp READ timestamp) - Q_PROPERTY(QString date READ date) - Q_PROPERTY(QString time READ time) - Q_PROPERTY(QString paymentId READ paymentId) - Q_PROPERTY(QString destinations_formatted READ destinations_formatted) - Q_PROPERTY(QString rings_formatted READ rings_formatted) public: enum Direction { - Direction_In = Monero::TransactionInfo::Direction_In, - Direction_Out = Monero::TransactionInfo::Direction_Out, + Direction_In = 0, + Direction_Out = 1, Direction_Both // invalid direction value, used for filtering }; @@ -80,7 +56,10 @@ public: QString rings_formatted() const; private: - explicit TransactionInfo(const Monero::TransactionInfo *pimpl, QObject *parent = nullptr); + explicit TransactionRow(); + +// TransactionRow(const Monero::TransactionInfo *pimpl, QObject *parent = nullptr); + private: friend class TransactionHistory; mutable QList m_transfers; @@ -104,4 +83,5 @@ private: bool m_coinbase; }; -#endif // TRANSACTIONINFO_H + +#endif //FEATHER_TRANSACTIONROW_H diff --git a/src/model/AddressBookModel.cpp b/src/model/AddressBookModel.cpp index 214daa55..fb664253 100644 --- a/src/model/AddressBookModel.cpp +++ b/src/model/AddressBookModel.cpp @@ -70,12 +70,12 @@ QVariant AddressBookModel::data(const QModelIndex &index, int role) const { QVariant result; - bool found = m_addressBook->getRow(index.row(), [this, &result, &role, &index](const AddressBookInfo &row) { + bool found = m_addressBook->getRow(index.row(), [this, &result, &role, &index](const ContactRow &row) { if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::UserRole) { switch (index.column()) { case Address: { - QString address = row.address(); + QString address = row.getAddress(); if (!m_showFullAddresses && role != Qt::UserRole) { address = Utils::displayAddress(address); } @@ -83,7 +83,7 @@ QVariant AddressBookModel::data(const QModelIndex &index, int role) const break; } case Description: - result = row.description(); + result = row.getLabel(); break; default: qCritical() << "Invalid column" << index.column(); diff --git a/src/model/CoinsModel.cpp b/src/model/CoinsModel.cpp index e69f51a5..27bb1dff 100644 --- a/src/model/CoinsModel.cpp +++ b/src/model/CoinsModel.cpp @@ -2,7 +2,7 @@ // SPDX-FileCopyrightText: 2020-2023 The Monero Project #include "CoinsModel.h" -#include "CoinsInfo.h" +#include "libwalletqt/rows/CoinsInfo.h" #include "Coins.h" #include "constants.h" #include "utils/ColorScheme.h" diff --git a/src/model/CoinsProxyModel.cpp b/src/model/CoinsProxyModel.cpp index 16e3a9dc..92077c54 100644 --- a/src/model/CoinsProxyModel.cpp +++ b/src/model/CoinsProxyModel.cpp @@ -3,7 +3,7 @@ #include "CoinsProxyModel.h" #include "CoinsModel.h" -#include "libwalletqt/CoinsInfo.h" +#include "libwalletqt/rows/CoinsInfo.h" CoinsProxyModel::CoinsProxyModel(QObject *parent, Coins *coins) : QSortFilterProxyModel(parent) diff --git a/src/model/HistoryView.cpp b/src/model/HistoryView.cpp index 4caefedb..9fe311df 100644 --- a/src/model/HistoryView.cpp +++ b/src/model/HistoryView.cpp @@ -4,7 +4,6 @@ #include "HistoryView.h" #include "TransactionHistoryProxyModel.h" -#include "libwalletqt/TransactionInfo.h" #include "utils/Utils.h" #include @@ -71,7 +70,7 @@ TransactionHistoryModel* HistoryView::sourceModel() return dynamic_cast(m_model->sourceModel()); } -TransactionInfo* HistoryView::currentEntry() +TransactionRow* HistoryView::currentEntry() { QModelIndexList list = selectionModel()->selectedRows(); if (list.size() == 1) { @@ -199,7 +198,7 @@ void HistoryView::resetViewToDefaults() } void HistoryView::keyPressEvent(QKeyEvent *event) { - TransactionInfo* tx = this->currentEntry(); + TransactionRow* tx = this->currentEntry(); if (event->matches(QKeySequence::Copy) && tx) { Utils::copyToClipboard(tx->hash()); diff --git a/src/model/HistoryView.h b/src/model/HistoryView.h index 5eea566b..bab00f35 100644 --- a/src/model/HistoryView.h +++ b/src/model/HistoryView.h @@ -18,7 +18,7 @@ class HistoryView : public QTreeView public: explicit HistoryView(QWidget* parent = nullptr); void setHistoryModel(TransactionHistoryProxyModel *model); - TransactionInfo* currentEntry(); + TransactionRow* currentEntry(); void setSearchMode(bool mode); QByteArray viewState() const; diff --git a/src/model/SubaddressAccountModel.cpp b/src/model/SubaddressAccountModel.cpp index 138d684b..f0cdff7f 100644 --- a/src/model/SubaddressAccountModel.cpp +++ b/src/model/SubaddressAccountModel.cpp @@ -8,6 +8,7 @@ #include "utils/Utils.h" #include "libwalletqt/WalletManager.h" +#include "rows/AccountRow.h" SubaddressAccountModel::SubaddressAccountModel(QObject *parent, SubaddressAccount *subaddressAccount) : QAbstractTableModel(parent) @@ -49,7 +50,7 @@ QVariant SubaddressAccountModel::data(const QModelIndex &index, int role) const QVariant result; - bool found = m_subaddressAccount->getRow(index.row(), [this, &index, &result, &role](const Monero::SubaddressAccountRow &row) { + bool found = m_subaddressAccount->getRow(index.row(), [this, &index, &result, &role](const AccountRow &row) { if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::UserRole) { result = parseSubaddressAccountRow(row, index, role); } @@ -72,7 +73,7 @@ QVariant SubaddressAccountModel::data(const QModelIndex &index, int role) const return result; } -QVariant SubaddressAccountModel::parseSubaddressAccountRow(const Monero::SubaddressAccountRow &row, +QVariant SubaddressAccountModel::parseSubaddressAccountRow(const AccountRow &row, const QModelIndex &index, int role) const { switch (index.column()) { @@ -82,19 +83,19 @@ QVariant SubaddressAccountModel::parseSubaddressAccountRow(const Monero::Subaddr } return QString("#%1").arg(QString::number(index.row())); case Address: - return QString::fromStdString(row.getAddress()); + return row.getAddress(); case Label: - return QString::fromStdString(row.getLabel()); + return row.getLabel(); case Balance: if (role == Qt::UserRole) { - return WalletManager::amountFromString(QString::fromStdString(row.getBalance())); + return WalletManager::amountFromString(row.getBalance()); } - return QString::fromStdString(row.getBalance()); + return row.getBalance(); case UnlockedBalance: if (role == Qt::UserRole) { - return WalletManager::amountFromString(QString::fromStdString(row.getUnlockedBalance())); + return WalletManager::amountFromString(row.getUnlockedBalance()); } - return QString::fromStdString(row.getUnlockedBalance()); + return row.getUnlockedBalance(); default: return QVariant(); } @@ -154,8 +155,7 @@ Qt::ItemFlags SubaddressAccountModel::flags(const QModelIndex &index) const return QAbstractTableModel::flags(index); } -Monero::SubaddressAccountRow* SubaddressAccountModel::entryFromIndex(const QModelIndex &index) const { - Q_ASSERT(index.isValid() && index.row() < m_subaddressAccount->count()); +AccountRow* SubaddressAccountModel::entryFromIndex(const QModelIndex &index) const { return m_subaddressAccount->row(index.row()); } diff --git a/src/model/SubaddressAccountModel.h b/src/model/SubaddressAccountModel.h index 7ce0bbca..98b27054 100644 --- a/src/model/SubaddressAccountModel.h +++ b/src/model/SubaddressAccountModel.h @@ -8,6 +8,8 @@ #include #include +#include "rows/AccountRow.h" + class SubaddressAccount; class SubaddressAccountModel : public QAbstractTableModel @@ -34,14 +36,14 @@ public: bool setData(const QModelIndex &index, const QVariant &value, int role) override; - Monero::SubaddressAccountRow* entryFromIndex(const QModelIndex &index) const; + AccountRow* entryFromIndex(const QModelIndex &index) const; public slots: void startReset(); void endReset(); private: - QVariant parseSubaddressAccountRow(const Monero::SubaddressAccountRow &row, const QModelIndex &index, int role) const; + QVariant parseSubaddressAccountRow(const AccountRow &row, const QModelIndex &index, int role) const; SubaddressAccount *m_subaddressAccount; }; diff --git a/src/model/TransactionHistoryModel.cpp b/src/model/TransactionHistoryModel.cpp index 0d690124..cd7c0024 100644 --- a/src/model/TransactionHistoryModel.cpp +++ b/src/model/TransactionHistoryModel.cpp @@ -3,13 +3,13 @@ #include "TransactionHistoryModel.h" #include "TransactionHistory.h" -#include "TransactionInfo.h" #include "constants.h" #include "utils/config.h" #include "utils/ColorScheme.h" #include "utils/Icons.h" #include "utils/AppData.h" #include "utils/Utils.h" +#include "libwalletqt/rows/TransactionRow.h" TransactionHistoryModel::TransactionHistoryModel(QObject *parent) : QAbstractTableModel(parent), @@ -34,7 +34,7 @@ TransactionHistory *TransactionHistoryModel::transactionHistory() const { return m_transactionHistory; } -TransactionInfo* TransactionHistoryModel::entryFromIndex(const QModelIndex &index) const { +TransactionRow* TransactionHistoryModel::entryFromIndex(const QModelIndex &index) const { Q_ASSERT(index.isValid() && index.row() < m_transactionHistory->count()); return m_transactionHistory->transaction(index.row()); } @@ -65,7 +65,7 @@ QVariant TransactionHistoryModel::data(const QModelIndex &index, int role) const QVariant result; - bool found = m_transactionHistory->transaction(index.row(), [this, &index, &result, &role](const TransactionInfo &tInfo) { + bool found = m_transactionHistory->transaction(index.row(), [this, &index, &result, &role](const TransactionRow &tInfo) { if(role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::UserRole) { result = parseTransactionInfo(tInfo, index.column(), role); } @@ -117,7 +117,7 @@ QVariant TransactionHistoryModel::data(const QModelIndex &index, int role) const case Column::FiatAmount: case Column::Amount: { - if (tInfo.direction() == TransactionInfo::Direction_Out) { + if (tInfo.direction() == TransactionRow::Direction_Out) { result = QVariant(QColor("#BC1E1E")); } } @@ -139,7 +139,7 @@ QVariant TransactionHistoryModel::data(const QModelIndex &index, int role) const return result; } -QVariant TransactionHistoryModel::parseTransactionInfo(const TransactionInfo &tInfo, int column, int role) const +QVariant TransactionHistoryModel::parseTransactionInfo(const TransactionRow &tInfo, int column, int role) const { switch (column) { @@ -159,7 +159,7 @@ QVariant TransactionHistoryModel::parseTransactionInfo(const TransactionInfo &tI return tInfo.balanceDelta(); } QString amount = QString::number(tInfo.balanceDelta() / constants::cdiv, 'f', conf()->get(Config::amountPrecision).toInt()); - amount = (tInfo.direction() == TransactionInfo::Direction_Out) ? "-" + amount : "+" + amount; + amount = (tInfo.direction() == TransactionRow::Direction_Out) ? "-" + amount : "+" + amount; return amount; } case Column::TxID: { @@ -227,7 +227,7 @@ bool TransactionHistoryModel::setData(const QModelIndex &index, const QVariant & switch (index.column()) { case Column::Description: { - m_transactionHistory->transaction(index.row(), [this, &hash, &value](const TransactionInfo &tInfo){ + m_transactionHistory->transaction(index.row(), [this, &hash, &value](const TransactionRow &tInfo){ hash = tInfo.hash(); }); m_transactionHistory->setTxNote(hash, value.toString()); diff --git a/src/model/TransactionHistoryModel.h b/src/model/TransactionHistoryModel.h index 582c57cc..a55f235d 100644 --- a/src/model/TransactionHistoryModel.h +++ b/src/model/TransactionHistoryModel.h @@ -8,7 +8,7 @@ #include class TransactionHistory; -class TransactionInfo; +class TransactionRow; /** * @brief The TransactionHistoryModel class - read-only table model for Transaction History @@ -33,7 +33,7 @@ public: explicit TransactionHistoryModel(QObject * parent = nullptr); void setTransactionHistory(TransactionHistory * th); TransactionHistory * transactionHistory() const; - TransactionInfo* entryFromIndex(const QModelIndex& index) const; + TransactionRow* entryFromIndex(const QModelIndex& index) const; int rowCount(const QModelIndex & parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; @@ -47,7 +47,7 @@ signals: void transactionHistoryChanged(); private: - QVariant parseTransactionInfo(const TransactionInfo &tInfo, int column, int role) const; + QVariant parseTransactionInfo(const TransactionRow &tInfo, int column, int role) const; TransactionHistory * m_transactionHistory; }; diff --git a/src/model/TransactionHistoryProxyModel.cpp b/src/model/TransactionHistoryProxyModel.cpp index e905c62c..83103733 100644 --- a/src/model/TransactionHistoryProxyModel.cpp +++ b/src/model/TransactionHistoryProxyModel.cpp @@ -4,7 +4,7 @@ #include "TransactionHistoryProxyModel.h" #include "TransactionHistoryModel.h" -#include "libwalletqt/TransactionInfo.h" +#include "libwalletqt/rows/TransactionRow.h" TransactionHistoryProxyModel::TransactionHistoryProxyModel(Wallet *wallet, QObject *parent) : QSortFilterProxyModel(parent) @@ -25,7 +25,7 @@ bool TransactionHistoryProxyModel::filterAcceptsRow(int sourceRow, const QModelI quint32 subaddrAccount; QSet subaddrIndex; - m_history->transaction(sourceRow, [&description, &txid, &subaddrlabel, &subaddrAccount, &subaddrIndex](TransactionInfo &tInfo){ + m_history->transaction(sourceRow, [&description, &txid, &subaddrlabel, &subaddrAccount, &subaddrIndex](TransactionRow &tInfo){ description = tInfo.description(); txid = tInfo.hash(); subaddrlabel = tInfo.label();