// SPDX-License-Identifier: BSD-3-Clause // SPDX-FileCopyrightText: 2020-2023 The Monero Project #include "Subaddress.h" #include #include Subaddress::Subaddress(Wallet *wallet, tools::wallet2 *wallet2, QObject *parent) : QObject(parent) , m_wallet(wallet) , m_wallet2(wallet2) { QString pinned = m_wallet->getCacheAttribute("feather.pinnedaddresses"); m_pinned = pinned.split(","); QString hidden = m_wallet->getCacheAttribute("feather.hiddenaddresses"); m_hidden = hidden.split(","); } bool Subaddress::getRow(int index, std::function callback) const { if (index < 0 || index >= m_rows.size()) { return false; } callback(*m_rows.value(index)); return true; } bool Subaddress::addRow(quint32 accountIndex, const QString &label) { // This can fail if hardware device is unplugged during operating, catch here to prevent crash // Todo: Notify GUI that it was a device error try { m_wallet2->add_subaddress(accountIndex, label.toStdString()); refresh(accountIndex); } catch (const std::exception& e) { if (m_wallet2->key_on_device()) { } m_errorString = QString::fromStdString(e.what()); return false; } return true; } bool Subaddress::setLabel(quint32 accountIndex, quint32 addressIndex, const QString &label) { try { m_wallet2->set_subaddress_label({accountIndex, addressIndex}, label.toStdString()); refresh(accountIndex); } catch (const std::exception& e) { return false; } return true; } bool Subaddress::setHidden(const QString &address, bool hidden) { if (hidden) { if (m_hidden.contains(address)) { return false; } m_hidden.append(address); } else { if (!m_hidden.contains(address)) { return false; } m_hidden.removeAll(address); } bool r = m_wallet->setCacheAttribute("feather.hiddenaddresses", m_hidden.join(",")); refresh(m_wallet->currentSubaddressAccount()); return r; } bool Subaddress::isHidden(const QString &address) { return m_hidden.contains(address); }; bool Subaddress::setPinned(const QString &address, bool pinned) { if (pinned) { if (m_pinned.contains(address)) { return false; } m_pinned.append(address); } else { if (!m_pinned.contains(address)) { return false; } m_pinned.removeAll(address); } bool r = m_wallet->setCacheAttribute("feather.pinnedaddresses", m_pinned.join(",")); refresh(m_wallet->currentSubaddressAccount()); return r; } bool Subaddress::isPinned(const QString &address) { return m_pinned.contains(address); } bool Subaddress::refresh(quint32 accountIndex) { emit refreshStarted(); this->clearRows(); for (qsizetype i = 0; i < m_wallet2->get_num_subaddresses(accountIndex); ++i) { QString address = QString::fromStdString(m_wallet2->get_subaddress_as_str({accountIndex, (uint32_t)i})); auto* row = new SubaddressRow{this, i, address, QString::fromStdString(m_wallet2->get_subaddress_label({accountIndex, (uint32_t)i})), m_wallet2->get_subaddress_used({accountIndex, (uint32_t)i}), this->isHidden(address), this->isPinned(address) }; m_rows.append(row); } // Make sure keys are intact. We NEVER want to display incorrect addresses in case of memory corruption. bool keysCorrupt = m_wallet2->get_device_type() == hw::device::SOFTWARE && !m_wallet2->verify_keys(); if (keysCorrupt) { clearRows(); LOG_ERROR("KEY INCONSISTENCY DETECTED, WALLET IS IN CORRUPT STATE."); } emit refreshFinished(); return !keysCorrupt; } qsizetype Subaddress::count() const { return m_rows.length(); } void Subaddress::clearRows() { qDeleteAll(m_rows); m_rows.clear(); } SubaddressRow* Subaddress::row(int index) const { return m_rows.value(index); } QString Subaddress::getError() const { return m_errorString; }