From e83babe3b85b22e2360a99f9827b2b0d107ad0fa Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Mon, 20 Feb 2023 17:45:32 -0500 Subject: wallet: Replace use of purpose strings with an enum Instead of storing and passing around fixed strings for the purpose of an address, use an enum. This also rationalizes the CAddressBookData struct, documenting all fields and making them public, and simplifying the representation to avoid bugs like https://github.com/bitcoin/bitcoin/pull/26761#discussion_r1134615114 and make it not possible to invalid address data like change addresses with labels. Co-authored-by: Ryan Ofsky --- src/qt/addresstablemodel.cpp | 42 ++++++++++++++++++++-------------------- src/qt/addresstablemodel.h | 11 ++++++++--- src/qt/editaddressdialog.cpp | 6 ++++-- src/qt/test/addressbooktests.cpp | 4 ++-- src/qt/test/wallettests.cpp | 2 +- src/qt/walletmodel.cpp | 13 ++++++------- src/qt/walletmodel.h | 2 +- 7 files changed, 43 insertions(+), 37 deletions(-) (limited to 'src/qt') diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index e402c51ac4..0d0f1a4d15 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -52,17 +53,16 @@ struct AddressTableEntryLessThan }; /* Determine address type from address purpose */ -static AddressTableEntry::Type translateTransactionType(const QString &strPurpose, bool isMine) +static AddressTableEntry::Type translateTransactionType(wallet::AddressPurpose purpose, bool isMine) { - AddressTableEntry::Type addressType = AddressTableEntry::Hidden; // "refund" addresses aren't shown, and change addresses aren't returned by getAddresses at all. - if (strPurpose == "send") - addressType = AddressTableEntry::Sending; - else if (strPurpose == "receive") - addressType = AddressTableEntry::Receiving; - else if (strPurpose == "unknown" || strPurpose == "") // if purpose not set, guess - addressType = (isMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending); - return addressType; + switch (purpose) { + case wallet::AddressPurpose::SEND: return AddressTableEntry::Sending; + case wallet::AddressPurpose::RECEIVE: return AddressTableEntry::Receiving; + case wallet::AddressPurpose::REFUND: return AddressTableEntry::Hidden; + // No default case to allow for compiler to warn + } + assert(false); } // Private implementation @@ -85,7 +85,7 @@ public: continue; } AddressTableEntry::Type addressType = translateTransactionType( - QString::fromStdString(address.purpose), address.is_mine); + address.purpose, address.is_mine); cachedAddressTable.append(AddressTableEntry(addressType, QString::fromStdString(address.name), QString::fromStdString(EncodeDestination(address.dest)))); @@ -97,7 +97,7 @@ public: std::sort(cachedAddressTable.begin(), cachedAddressTable.end(), AddressTableEntryLessThan()); } - void updateEntry(const QString &address, const QString &label, bool isMine, const QString &purpose, int status) + void updateEntry(const QString &address, const QString &label, bool isMine, wallet::AddressPurpose purpose, int status) { // Find address / label in model QList::iterator lower = std::lower_bound( @@ -239,7 +239,7 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, if(!index.isValid()) return false; AddressTableEntry *rec = static_cast(index.internalPointer()); - std::string strPurpose = (rec->type == AddressTableEntry::Sending ? "send" : "receive"); + wallet::AddressPurpose purpose = rec->type == AddressTableEntry::Sending ? wallet::AddressPurpose::SEND : wallet::AddressPurpose::RECEIVE; editStatus = OK; if(role == Qt::EditRole) @@ -253,7 +253,7 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, editStatus = NO_CHANGES; return false; } - walletModel->wallet().setAddressBook(curAddress, value.toString().toStdString(), strPurpose); + walletModel->wallet().setAddressBook(curAddress, value.toString().toStdString(), purpose); } else if(index.column() == Address) { CTxDestination newAddress = DecodeDestination(value.toString().toStdString()); // Refuse to set invalid address, set error status and return false @@ -282,7 +282,7 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, // Remove old entry walletModel->wallet().delAddressBook(curAddress); // Add new entry with new address - walletModel->wallet().setAddressBook(newAddress, value.toString().toStdString(), strPurpose); + walletModel->wallet().setAddressBook(newAddress, value.toString().toStdString(), purpose); } } return true; @@ -334,7 +334,7 @@ QModelIndex AddressTableModel::index(int row, int column, const QModelIndex &par } void AddressTableModel::updateEntry(const QString &address, - const QString &label, bool isMine, const QString &purpose, int status) + const QString &label, bool isMine, wallet::AddressPurpose purpose, int status) { // Update address book model from Bitcoin core priv->updateEntry(address, label, isMine, purpose, status); @@ -365,7 +365,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con } // Add entry - walletModel->wallet().setAddressBook(DecodeDestination(strAddress), strLabel, "send"); + walletModel->wallet().setAddressBook(DecodeDestination(strAddress), strLabel, wallet::AddressPurpose::SEND); } else if(type == Receive) { @@ -416,18 +416,18 @@ QString AddressTableModel::labelForAddress(const QString &address) const return QString(); } -QString AddressTableModel::purposeForAddress(const QString &address) const +std::optional AddressTableModel::purposeForAddress(const QString &address) const { - std::string purpose; + wallet::AddressPurpose purpose; if (getAddressData(address, /* name= */ nullptr, &purpose)) { - return QString::fromStdString(purpose); + return purpose; } - return QString(); + return std::nullopt; } bool AddressTableModel::getAddressData(const QString &address, std::string* name, - std::string* purpose) const { + wallet::AddressPurpose* purpose) const { CTxDestination destination = DecodeDestination(address.toStdString()); return walletModel->wallet().getAddress(destination, name, /* is_mine= */ nullptr, purpose); } diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h index 6cc14654ef..599aa89cad 100644 --- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -5,6 +5,8 @@ #ifndef BITCOIN_QT_ADDRESSTABLEMODEL_H #define BITCOIN_QT_ADDRESSTABLEMODEL_H +#include + #include #include @@ -16,6 +18,9 @@ class WalletModel; namespace interfaces { class Wallet; } +namespace wallet { +enum class AddressPurpose; +} // namespace wallet /** Qt model of the address book in the core. This allows views to access and modify the address book. @@ -71,7 +76,7 @@ public: QString labelForAddress(const QString &address) const; /** Look up purpose for address in address book, if not found return empty string. */ - QString purposeForAddress(const QString &address) const; + std::optional purposeForAddress(const QString &address) const; /* Look up row index of an address in the model. Return -1 if not found. @@ -89,7 +94,7 @@ private: EditStatus editStatus = OK; /** Look up address book data given an address string. */ - bool getAddressData(const QString &address, std::string* name, std::string* purpose) const; + bool getAddressData(const QString &address, std::string* name, wallet::AddressPurpose* purpose) const; /** Notify listeners that data changed. */ void emitDataChanged(int index); @@ -97,7 +102,7 @@ private: public Q_SLOTS: /* Update address list from core. */ - void updateEntry(const QString &address, const QString &label, bool isMine, const QString &purpose, int status); + void updateEntry(const QString &address, const QString &label, bool isMine, wallet::AddressPurpose purpose, int status); friend class AddressTablePriv; }; diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp index 9b3319415d..092a89fa11 100644 --- a/src/qt/editaddressdialog.cpp +++ b/src/qt/editaddressdialog.cpp @@ -8,6 +8,8 @@ #include #include +#include + #include #include @@ -137,9 +139,9 @@ QString EditAddressDialog::getDuplicateAddressWarning() const { QString dup_address = ui->addressEdit->text(); QString existing_label = model->labelForAddress(dup_address); - QString existing_purpose = model->purposeForAddress(dup_address); + auto existing_purpose = model->purposeForAddress(dup_address); - if (existing_purpose == "receive" && + if (existing_purpose == wallet::AddressPurpose::RECEIVE && (mode == NewSendingAddress || mode == EditSendingAddress)) { return tr( "Address \"%1\" already exists as a receiving address with label " diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp index d005e08d14..5706964cc9 100644 --- a/src/qt/test/addressbooktests.cpp +++ b/src/qt/test/addressbooktests.cpp @@ -113,8 +113,8 @@ void TestAddAddressesToSendBook(interfaces::Node& node) { LOCK(wallet->cs_wallet); - wallet->SetAddressBook(r_key_dest, r_label.toStdString(), "receive"); - wallet->SetAddressBook(s_key_dest, s_label.toStdString(), "send"); + wallet->SetAddressBook(r_key_dest, r_label.toStdString(), wallet::AddressPurpose::RECEIVE); + wallet->SetAddressBook(s_key_dest, s_label.toStdString(), wallet::AddressPurpose::SEND); } auto check_addbook_size = [&wallet](int expected_size) { diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index eb7bf33a32..072718fe15 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -221,7 +221,7 @@ std::shared_ptr SetupDescriptorsWallet(interfaces::Node& node, TestChai WalletDescriptor w_desc(std::move(desc), 0, 0, 1, 1); if (!wallet->AddWalletDescriptor(w_desc, provider, "", false)) assert(false); CTxDestination dest = GetDestinationForKey(test.coinbaseKey.GetPubKey(), wallet->m_default_address_type); - wallet->SetAddressBook(dest, "", "receive"); + wallet->SetAddressBook(dest, "", wallet::AddressPurpose::RECEIVE); wallet->SetLastBlockProcessed(105, WITH_LOCK(node.context()->chainman->GetMutex(), return node.context()->chainman->ActiveChain().Tip()->GetBlockHash())); SyncUpWallet(wallet, node); wallet->SetBroadcastTransactions(true); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 565b732bf0..fdd96c664a 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -138,7 +138,7 @@ void WalletModel::updateTransaction() } void WalletModel::updateAddressBook(const QString &address, const QString &label, - bool isMine, const QString &purpose, int status) + bool isMine, wallet::AddressPurpose purpose, int status) { if(addressTableModel) addressTableModel->updateEntry(address, label, isMine, purpose, status); @@ -280,11 +280,11 @@ void WalletModel::sendCoins(WalletModelTransaction& transaction) if (!m_wallet->getAddress( dest, &name, /* is_mine= */ nullptr, /* purpose= */ nullptr)) { - m_wallet->setAddressBook(dest, strLabel, "send"); + m_wallet->setAddressBook(dest, strLabel, wallet::AddressPurpose::SEND); } else if (name != strLabel) { - m_wallet->setAddressBook(dest, strLabel, ""); // "" means don't change purpose + m_wallet->setAddressBook(dest, strLabel, {}); // {} means don't change purpose } } } @@ -377,18 +377,17 @@ static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel) static void NotifyAddressBookChanged(WalletModel *walletmodel, const CTxDestination &address, const std::string &label, bool isMine, - const std::string &purpose, ChangeType status) + wallet::AddressPurpose purpose, ChangeType status) { QString strAddress = QString::fromStdString(EncodeDestination(address)); QString strLabel = QString::fromStdString(label); - QString strPurpose = QString::fromStdString(purpose); - qDebug() << "NotifyAddressBookChanged: " + strAddress + " " + strLabel + " isMine=" + QString::number(isMine) + " purpose=" + strPurpose + " status=" + QString::number(status); + qDebug() << "NotifyAddressBookChanged: " + strAddress + " " + strLabel + " isMine=" + QString::number(isMine) + " purpose=" + QString::number(static_cast(purpose)) + " status=" + QString::number(status); bool invoked = QMetaObject::invokeMethod(walletmodel, "updateAddressBook", Q_ARG(QString, strAddress), Q_ARG(QString, strLabel), Q_ARG(bool, isMine), - Q_ARG(QString, strPurpose), + Q_ARG(wallet::AddressPurpose, purpose), Q_ARG(int, status)); assert(invoked); } diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 17a39349f3..4f75d41404 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -236,7 +236,7 @@ public Q_SLOTS: /* New transaction, or transaction changed status */ void updateTransaction(); /* New, updated or removed address book entry */ - void updateAddressBook(const QString &address, const QString &label, bool isMine, const QString &purpose, int status); + void updateAddressBook(const QString &address, const QString &label, bool isMine, wallet::AddressPurpose purpose, int status); /* Watch-only added */ void updateWatchOnlyFlag(bool fHaveWatchonly); /* Current, immature or unconfirmed balance might have changed - emit 'balanceChanged' if so */ -- cgit v1.2.3