From 698658e32f016d03b1638b5964031cff7f4fd9c6 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 12 Aug 2021 20:31:35 +0000 Subject: [PATCH 01/13] Wallet: Keep track of what addresses are used in wallet transactions (memory only) --- src/wallet/wallet.cpp | 25 ++++++++++++++++++++++++- src/wallet/wallet.h | 4 ++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 7e694d1987d..ebb5fbd35f6 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -656,6 +656,23 @@ void CWallet::AddToSpends(const uint256& wtxid, WalletBatch* batch) AddToSpends(txin.prevout, wtxid, batch); } +void CWallet::UpdateAddressBookUsed(const CWalletTx& wtx) +{ + for (const auto& output : wtx.tx->vout) { + CTxDestination dest; + if (!ExtractDestination(output.scriptPubKey, dest)) continue; + m_address_book[dest].m_used = true; + } +} + +void CWallet::InitialiseAddressBookUsed() +{ + for (const auto& entry : mapWallet) { + const CWalletTx& wtx = entry.second; + UpdateAddressBookUsed(wtx); + } +} + bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) { if (IsCrypted()) @@ -953,6 +970,7 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const TxState& state, const wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx)); wtx.nTimeSmart = ComputeTimeSmart(wtx, rescanning_old_block); AddToSpends(hash, &batch); + UpdateAddressBookUsed(wtx); } if (!fInsertedNew) @@ -2133,7 +2151,12 @@ DBErrors CWallet::LoadWallet() assert(m_internal_spk_managers.empty()); } - return nLoadWalletRet; + if (nLoadWalletRet != DBErrors::LOAD_OK) + return nLoadWalletRet; + + InitialiseAddressBookUsed(); + + return DBErrors::LOAD_OK; } DBErrors CWallet::ZapSelectTx(std::vector& vHashIn, std::vector& vHashOut) diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index e2c5c69c91e..5431ec00d08 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -204,6 +204,7 @@ class CAddressBookData std::string m_label; public: std::string purpose; + bool m_used{false}; CAddressBookData() : purpose("unknown") {} @@ -263,6 +264,9 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati void AddToSpends(const COutPoint& outpoint, const uint256& wtxid, WalletBatch* batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void AddToSpends(const uint256& wtxid, WalletBatch* batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + void UpdateAddressBookUsed(const CWalletTx&) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + void InitialiseAddressBookUsed() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + /** * Add a transaction to the wallet, or update it. confirm.block_* should * be set when the transaction was known to be included in a block. When From 2e2d887be15aa4a26facf66087d9a4716cfeb5aa Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 12 Aug 2021 20:52:10 +0000 Subject: [PATCH 02/13] Wallet: Add fairly-efficient [negative] check that an address is not known to be used --- src/wallet/wallet.cpp | 37 +++++++++++++++++++++++++++++++++++++ src/wallet/wallet.h | 3 +++ 2 files changed, 40 insertions(+) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index ebb5fbd35f6..bc72fc6deba 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include @@ -673,6 +674,42 @@ void CWallet::InitialiseAddressBookUsed() } } +bool CWallet::FindScriptPubKeyUsed(const std::set& keys, const std::variant, std::function>& callback) const +{ + bool found_any = false; + for (const auto& key : keys) { + CTxDestination dest; + if (!ExtractDestination(key, dest)) continue; + const auto& address_book_it = m_address_book.find(dest); + if (address_book_it == m_address_book.end()) continue; + if (address_book_it->second.m_used) { + found_any = true; + break; + } + } + if (!found_any) return false; + if (std::holds_alternative(callback)) return true; + + found_any = false; + for (const auto& entry : mapWallet) { + const CWalletTx& wtx = entry.second; + for (uint32_t i = 0; i < wtx.tx->vout.size(); ++i) { + const auto& output = wtx.tx->vout[i]; + if (keys.count(output.scriptPubKey)) { + found_any = true; + const auto callback_type = callback.index(); + if (callback_type == 1) { + std::get>(callback)(wtx); + break; + } + std::get>(callback)(wtx, i); + } + } + } + + return found_any; +} + bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) { if (IsCrypted()) diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 5431ec00d08..5bd8aa8ef39 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -467,6 +468,8 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati bool UnlockAllCoins() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void ListLockedCoins(std::vector& vOutpts) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool FindScriptPubKeyUsed(const std::set& keys, const std::variant, std::function>& callback = std::monostate()) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + /* * Rescan abort properties */ From 4a9a72b3eb3ecbef0f5569c71335ee53db212fb1 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sun, 5 May 2019 04:10:54 +0000 Subject: [PATCH 03/13] RPC/Wallet: Add "use_txids" to output of getaddressinfo --- src/wallet/rpc/addresses.cpp | 13 +++++++++++++ test/functional/wallet_basic.py | 6 ++++++ 2 files changed, 19 insertions(+) diff --git a/src/wallet/rpc/addresses.cpp b/src/wallet/rpc/addresses.cpp index 51587a64a39..3c23447a1f3 100644 --- a/src/wallet/rpc/addresses.cpp +++ b/src/wallet/rpc/addresses.cpp @@ -541,6 +541,10 @@ RPCHelpMan getaddressinfo() { {RPCResult::Type::STR, "label name", "Label name (defaults to \"\")."}, }}, + {RPCResult::Type::ARR, "use_txids", "", + { + {RPCResult::Type::STR_HEX, "txid", "The ids of transactions involving this wallet which received with the address"}, + }}, } }, RPCExamples{ @@ -632,6 +636,15 @@ RPCHelpMan getaddressinfo() } ret.pushKV("labels", std::move(labels)); + // NOTE: Intentionally not special-casing a single txid: while addresses + // should never be reused, it's not unexpected to have RBF result in + // multiple txids for a single use. + UniValue use_txids(UniValue::VARR); + pwallet->FindScriptPubKeyUsed(std::set{scriptPubKey}, [&use_txids](const CWalletTx&wtx) { + use_txids.push_back(wtx.GetHash().GetHex()); + }); + ret.pushKV("use_txids", std::move(use_txids)); + return ret; }, }; diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py index 69f9df57d89..4acc9b065e9 100755 --- a/test/functional/wallet_basic.py +++ b/test/functional/wallet_basic.py @@ -642,6 +642,12 @@ def run_test(self): assert not address_info["iswatchonly"] assert not address_info["isscript"] assert not address_info["ischange"] + assert_equal(address_info['use_txids'], []) + + # Test getaddressinfo 'txids' field + txid = self.nodes[0].sendtoaddress("mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ", 1) + address_info = self.nodes[0].getaddressinfo("mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ") + assert_equal(address_info['use_txids'], [txid]) # Test getaddressinfo 'ischange' field on change address. self.generate(self.nodes[0], 1, sync_fun=self.no_op) From 8719b084754b8346e1cb551843afd3afcd439b71 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 19 Oct 2021 03:34:09 +0000 Subject: [PATCH 04/13] Bugfix: Wallet: Don't initialise "used" flag for wallet tool "info" command Initialising "used" would otherwise incorrectly inflate the address book count beyond what was actually in the file --- src/wallet/wallet.cpp | 4 ++-- src/wallet/wallet.h | 3 ++- src/wallet/wallettool.cpp | 7 ++++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index bc72fc6deba..9e6a6858edb 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2168,7 +2168,7 @@ void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve } } -DBErrors CWallet::LoadWallet() +DBErrors CWallet::LoadWallet(const do_init_used_flag do_init_used_flag_val) { LOCK(cs_wallet); @@ -2191,7 +2191,7 @@ DBErrors CWallet::LoadWallet() if (nLoadWalletRet != DBErrors::LOAD_OK) return nLoadWalletRet; - InitialiseAddressBookUsed(); + if (do_init_used_flag_val == do_init_used_flag::Init) InitialiseAddressBookUsed(); return DBErrors::LOAD_OK; } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 5bd8aa8ef39..55c0f27dcc0 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -673,7 +673,8 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const; void chainStateFlushed(const CBlockLocator& loc) override; - DBErrors LoadWallet(); + enum class do_init_used_flag { Init, Skip }; + DBErrors LoadWallet(const do_init_used_flag do_init_used_flag_val = do_init_used_flag::Init); DBErrors ZapSelectTx(std::vector& vHashIn, std::vector& vHashOut) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& purpose); diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp index 9cd18dd0a5c..0b4a07c62fa 100644 --- a/src/wallet/wallettool.cpp +++ b/src/wallet/wallettool.cpp @@ -47,7 +47,7 @@ static void WalletCreate(CWallet* wallet_instance, uint64_t wallet_creation_flag wallet_instance->TopUpKeyPool(); } -static const std::shared_ptr MakeWallet(const std::string& name, const fs::path& path, const ArgsManager& args, DatabaseOptions options) +static const std::shared_ptr MakeWallet(const std::string& name, const fs::path& path, const ArgsManager& args, DatabaseOptions options, CWallet::do_init_used_flag do_init_used_flag_val = CWallet::do_init_used_flag::Init) { DatabaseStatus status; bilingual_str error; @@ -61,7 +61,7 @@ static const std::shared_ptr MakeWallet(const std::string& name, const std::shared_ptr wallet_instance{new CWallet(nullptr /* chain */, name, args, std::move(database)), WalletToolReleaseWallet}; DBErrors load_wallet_ret; try { - load_wallet_ret = wallet_instance->LoadWallet(); + load_wallet_ret = wallet_instance->LoadWallet(do_init_used_flag_val); } catch (const std::runtime_error&) { tfm::format(std::cerr, "Error loading %s. Is wallet being used by another process?\n", name); return nullptr; @@ -166,7 +166,8 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command) } else if (command == "info") { DatabaseOptions options; options.require_existing = true; - const std::shared_ptr wallet_instance = MakeWallet(name, path, args, options); + // NOTE: We need to skip initialisation of the m_used flag, or else the address book count might be wrong + const std::shared_ptr wallet_instance = MakeWallet(name, path, args, options, CWallet::do_init_used_flag::Skip); if (!wallet_instance) return false; WalletShowInfo(wallet_instance.get()); wallet_instance->Close(); From 3cce58ee4894ea8f548d58be1a0612a72812afca Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 7 May 2019 13:15:46 +0000 Subject: [PATCH 05/13] interfaces/wallet: Add checkAddressForUsage and findAddressUsage --- src/interfaces/wallet.h | 3 +++ src/wallet/interfaces.cpp | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h index f26ac866dcf..572c9eeeff5 100644 --- a/src/interfaces/wallet.h +++ b/src/interfaces/wallet.h @@ -125,6 +125,9 @@ class Wallet //! Display address on external signer virtual bool displayAddress(const CTxDestination& dest) = 0; + virtual bool checkAddressForUsage(const std::vector& addresses) const = 0; + virtual bool findAddressUsage(const std::vector& addresses, std::function callback) const = 0; + //! Lock coin. virtual bool lockCoin(const COutPoint& output, const bool write_to_db) = 0; diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp index 9083c304b2c..67f401e61b3 100644 --- a/src/wallet/interfaces.cpp +++ b/src/wallet/interfaces.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,7 @@ #include #include +#include #include #include #include @@ -47,6 +49,16 @@ using interfaces::WalletTxOut; using interfaces::WalletTxStatus; using interfaces::WalletValueMap; +std::set AddressesToKeys(std::vector addresses) +{ + std::set keys; + for (const auto& address : addresses) { + CScript scriptPubKey = GetScriptForDestination(DecodeDestination(address)); + keys.insert(scriptPubKey); + } + return keys; +} + namespace wallet { namespace { //! Construct wallet tx struct. @@ -216,6 +228,23 @@ class WalletImpl : public Wallet LOCK(m_wallet->cs_wallet); return m_wallet->DisplayAddress(dest); } + bool checkAddressForUsage(const std::vector& addresses) const override + { + LOCK(m_wallet->cs_wallet); + return m_wallet->FindScriptPubKeyUsed(AddressesToKeys(addresses)); + } + bool findAddressUsage(const std::vector& addresses, std::function callback) const override + { + LOCK(m_wallet->cs_wallet); + return m_wallet->FindScriptPubKeyUsed(AddressesToKeys(addresses), [&callback, this](const CWalletTx& wtx, uint32_t output_index){ + CTxDestination dest; + bool success = ExtractDestination(wtx.tx->vout[output_index].scriptPubKey, dest); + assert(success); // It shouldn't be possible to end up here with anything unrecognised + std::string address = EncodeDestination(dest); + WalletTx interface_wtx = MakeWalletTx(*m_wallet, wtx); + callback(address, interface_wtx, output_index); + }); + } bool lockCoin(const COutPoint& output, const bool write_to_db) override { LOCK(m_wallet->cs_wallet); From add7f99b0d6d49ca919807ffc89d50a956d92695 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Wed, 8 May 2019 19:49:40 +0000 Subject: [PATCH 06/13] GUI: Add GUIUtil::dateStr --- src/qt/guiutil.cpp | 12 +++++++++++- src/qt/guiutil.h | 2 ++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 9565fa508f1..41b5ba2323d 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -81,9 +81,19 @@ void ForceActivation(); namespace GUIUtil { +QString dateStr(const QDate &date) +{ + return QLocale::system().toString(date, QLocale::ShortFormat); +} + +QString dateStr(qint64 nTime) +{ + return dateStr(QDateTime::fromSecsSinceEpoch(nTime).date()); +} + QString dateTimeStr(const QDateTime &date) { - return QLocale::system().toString(date.date(), QLocale::ShortFormat) + QString(" ") + date.toString("hh:mm"); + return dateStr(date.date()) + QString(" ") + date.toString("hh:mm"); } QString dateTimeStr(qint64 nTime) diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 0224b18b4ee..69258972413 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -60,6 +60,8 @@ namespace GUIUtil constexpr auto dialog_flags = Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint; // Create human-readable string from date + QString dateStr(const QDate &datetime); + QString dateStr(qint64 nTime); QString dateTimeStr(const QDateTime &datetime); QString dateTimeStr(qint64 nTime); From 0301e4ce06456dc962e5244a895285838d40a403 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 12 Aug 2021 21:42:50 +0000 Subject: [PATCH 07/13] GUI: SendConfirmationDialog: Defer button setup until exec --- src/qt/sendcoinsdialog.cpp | 15 ++++++++------- src/qt/sendcoinsdialog.h | 1 + 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 579ef0c3fd5..126f6156d2f 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -1045,15 +1045,19 @@ void SendCoinsDialog::coinControlUpdateLabels() } SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text, const QString& detailed_text, int _secDelay, bool enable_send, bool always_show_unsigned, QWidget* parent) - : QMessageBox(parent), secDelay(_secDelay), m_enable_send(enable_send) + : QMessageBox(parent), secDelay(_secDelay), m_enable_save(always_show_unsigned || !enable_send), m_enable_send(enable_send) { setIcon(QMessageBox::Question); setWindowTitle(title); // On macOS, the window title is ignored (as required by the macOS Guidelines). setText(text); setInformativeText(informative_text); setDetailedText(detailed_text); +} + +int SendConfirmationDialog::exec() +{ setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); - if (always_show_unsigned || !enable_send) addButton(QMessageBox::Save); + if (m_enable_save) addButton(QMessageBox::Save); setDefaultButton(QMessageBox::Cancel); yesButton = button(QMessageBox::Yes); if (confirmButtonText.isEmpty()) { @@ -1061,13 +1065,10 @@ SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QStri } m_psbt_button = button(QMessageBox::Save); updateButtons(); - connect(&countDownTimer, &QTimer::timeout, this, &SendConfirmationDialog::countDown); -} -int SendConfirmationDialog::exec() -{ - updateButtons(); + connect(&countDownTimer, &QTimer::timeout, this, &SendConfirmationDialog::countDown); countDownTimer.start(1s); + return QMessageBox::exec(); } diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index 4a167027560..8f5993ae9b3 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -129,6 +129,7 @@ private Q_SLOTS: QTimer countDownTimer; int secDelay; QString confirmButtonText{tr("Send")}; + bool m_enable_save; bool m_enable_send; QString m_psbt_button_text{tr("Create Unsigned")}; }; From a73a126cf131ceb5267ce083f23e1540fd2d629b Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 29 Aug 2019 00:39:48 +0000 Subject: [PATCH 08/13] GUI: SendConfirmationDialog: Enable changing the actual buttons used Both buttons can be replaced with other standard buttons --- src/qt/sendcoinsdialog.cpp | 40 ++++++++++++++++++++++++++++++++----- src/qt/sendcoinsdialog.h | 4 ++++ src/qt/test/wallettests.cpp | 9 ++++++++- src/qt/walletmodel.cpp | 2 +- 4 files changed, 48 insertions(+), 7 deletions(-) diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 126f6156d2f..145624fae60 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -412,7 +412,7 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked) const QString confirmation = tr("Confirm send coins"); auto confirmationDialog = new SendConfirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, !model->wallet().privateKeysDisabled(), model->getOptionsModel()->getEnablePSBTControls(), this); - confirmationDialog->setAttribute(Qt::WA_DeleteOnClose); + confirmationDialog->m_delete_on_close = true; // TODO: Replace QDialog::exec() with safer QDialog::show(). const auto retval = static_cast(confirmationDialog->exec()); @@ -1056,10 +1056,26 @@ SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QStri int SendConfirmationDialog::exec() { - setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); + setStandardButtons(m_yes_button | m_cancel_button); + + yesButton = button(m_yes_button); + QAbstractButton * const cancel_button_obj = button(m_cancel_button); + + if (m_yes_button != QMessageBox::Yes || m_cancel_button != QMessageBox::Cancel) { + // We need to ensure the buttons have Yes/No roles, or they'll get ordered weird + // But only do it for customised yes/cancel buttons, so simple code can check results simply too + removeButton(cancel_button_obj); + addButton(cancel_button_obj, QMessageBox::NoRole); + setEscapeButton(cancel_button_obj); + + removeButton(yesButton); + addButton(yesButton, QMessageBox::YesRole); + } + if (m_enable_save) addButton(QMessageBox::Save); - setDefaultButton(QMessageBox::Cancel); - yesButton = button(QMessageBox::Yes); + + setDefaultButton(m_cancel_button); + if (confirmButtonText.isEmpty()) { confirmButtonText = yesButton->text(); } @@ -1069,7 +1085,21 @@ int SendConfirmationDialog::exec() connect(&countDownTimer, &QTimer::timeout, this, &SendConfirmationDialog::countDown); countDownTimer.start(1s); - return QMessageBox::exec(); + QMessageBox::exec(); + + int rv; + const auto clicked_button = clickedButton(); + if (clicked_button == m_psbt_button) { + rv = QMessageBox::Save; + } else if (clicked_button == yesButton) { + rv = QMessageBox::Yes; + } else { + rv = QMessageBox::Cancel; + } + + if (m_delete_on_close) delete this; + + return rv; } void SendConfirmationDialog::countDown() diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index 8f5993ae9b3..352cfaae5fa 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -116,6 +116,10 @@ class SendConfirmationDialog : public QMessageBox Q_OBJECT public: + bool m_delete_on_close{false}; + QMessageBox::StandardButton m_yes_button{QMessageBox::Yes}; + QMessageBox::StandardButton m_cancel_button{QMessageBox::Cancel}; + SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text = "", const QString& detailed_text = "", int secDelay = SEND_CONFIRM_DELAY, bool enable_send = true, bool always_show_unsigned = true, QWidget* parent = nullptr); int exec() override; diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index 6ab534764b6..0fcb25b887d 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -59,7 +59,14 @@ void ConfirmSend(QString* text = nullptr, bool cancel = false) if (widget->inherits("SendConfirmationDialog")) { SendConfirmationDialog* dialog = qobject_cast(widget); if (text) *text = dialog->text(); - QAbstractButton* button = dialog->button(cancel ? QMessageBox::Cancel : QMessageBox::Yes); + QAbstractButton* button = nullptr; + for (QAbstractButton* maybe_button : dialog->buttons()) { + const bool is_yes_button = (dialog->buttonRole(maybe_button) == QMessageBox::YesRole); + if (is_yes_button != cancel) { + button = maybe_button; + break; + } + } button->setEnabled(true); button->click(); } diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 5ee32e79d5d..7f1210d51df 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -509,7 +509,7 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash) } auto confirmationDialog = new SendConfirmationDialog(tr("Confirm fee bump"), questionString, "", "", SEND_CONFIRM_DELAY, !m_wallet->privateKeysDisabled(), getOptionsModel()->getEnablePSBTControls(), nullptr); - confirmationDialog->setAttribute(Qt::WA_DeleteOnClose); + confirmationDialog->m_delete_on_close = true; // TODO: Replace QDialog::exec() with safer QDialog::show(). const auto retval = static_cast(confirmationDialog->exec()); From 3fd599686bdaf6fab11d73defd20b825076d4ae3 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 7 May 2019 13:15:59 +0000 Subject: [PATCH 09/13] GUI: WalletModel: Wrap checkAddressForUsage and findAddressUsage --- src/qt/walletmodel.cpp | 16 ++++++++++++++++ src/qt/walletmodel.h | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 7f1210d51df..d29987332e9 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -150,6 +150,22 @@ bool WalletModel::validateAddress(const QString &address) return IsValidDestinationString(address.toStdString()); } +bool WalletModel::checkAddressForUsage(const std::vector& addresses) const +{ + return m_wallet->checkAddressForUsage(addresses); +} + +bool WalletModel::findAddressUsage(const QStringList& addresses, std::function callback) const +{ + std::vector std_addresses; + for (const auto& address : addresses) { + std_addresses.push_back(address.toStdString()); + } + return m_wallet->findAddressUsage(std_addresses, [&callback](const std::string& address, const interfaces::WalletTx& wtx, uint32_t output_index){ + callback(QString::fromStdString(address), wtx, output_index); + }); +} + WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl& coinControl) { CAmount total = 0; diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index ad1239ccdcd..73feb71c4a3 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -10,6 +10,7 @@ #endif #include +#include #include