diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/interface/node.cpp | 18 | ||||
-rw-r--r-- | src/interface/node.h | 10 | ||||
-rw-r--r-- | src/interface/wallet.cpp | 212 | ||||
-rw-r--r-- | src/interface/wallet.h | 200 | ||||
-rw-r--r-- | src/qt/bitcoin.cpp | 6 | ||||
-rw-r--r-- | src/qt/bitcoingui.cpp | 4 | ||||
-rw-r--r-- | src/qt/coincontroldialog.cpp | 12 | ||||
-rw-r--r-- | src/qt/overviewpage.cpp | 8 | ||||
-rw-r--r-- | src/qt/receivecoinsdialog.cpp | 6 | ||||
-rw-r--r-- | src/qt/sendcoinsdialog.cpp | 16 | ||||
-rw-r--r-- | src/qt/signverifymessagedialog.cpp | 2 | ||||
-rw-r--r-- | src/qt/test/wallettests.cpp | 6 | ||||
-rw-r--r-- | src/qt/transactionview.cpp | 10 | ||||
-rw-r--r-- | src/qt/walletmodel.cpp | 306 | ||||
-rw-r--r-- | src/qt/walletmodel.h | 59 | ||||
-rw-r--r-- | src/qt/walletmodeltransaction.cpp | 20 | ||||
-rw-r--r-- | src/qt/walletmodeltransaction.h | 15 | ||||
-rw-r--r-- | src/qt/walletview.cpp | 2 |
18 files changed, 585 insertions, 327 deletions
diff --git a/src/interface/node.cpp b/src/interface/node.cpp index 2f0114f5e4..db03cc0627 100644 --- a/src/interface/node.cpp +++ b/src/interface/node.cpp @@ -5,6 +5,7 @@ #include <interface/node.h> #include <addrdb.h> +#include <amount.h> #include <chain.h> #include <chainparams.h> #include <init.h> @@ -28,6 +29,7 @@ #include <config/bitcoin-config.h> #endif #ifdef ENABLE_WALLET +#include <wallet/wallet.h> #define CHECK_WALLET(x) x #else #define CHECK_WALLET(x) throw std::logic_error("Wallet function called in non-wallet build.") @@ -37,8 +39,6 @@ #include <boost/thread/thread.hpp> #include <univalue.h> -class CWallet; - namespace interface { namespace { @@ -185,6 +185,8 @@ class NodeImpl : public Node } } bool getNetworkActive() override { return g_connman && g_connman->GetNetworkActive(); } + unsigned int getTxConfirmTarget() override { CHECK_WALLET(return ::nTxConfirmTarget); } + CAmount getMaxTxFee() override { return ::maxTxFee; } UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) override { JSONRPCRequest req; @@ -196,6 +198,18 @@ class NodeImpl : public Node std::vector<std::string> listRpcCommands() override { return ::tableRPC.listCommands(); } void rpcSetTimerInterfaceIfUnset(RPCTimerInterface* iface) override { RPCSetTimerInterfaceIfUnset(iface); } void rpcUnsetTimerInterface(RPCTimerInterface* iface) override { RPCUnsetTimerInterface(iface); } + std::vector<std::unique_ptr<Wallet>> getWallets() override + { +#ifdef ENABLE_WALLET + std::vector<std::unique_ptr<Wallet>> wallets; + for (CWalletRef wallet : ::vpwallets) { + wallets.emplace_back(MakeWallet(*wallet)); + } + return wallets; +#else + throw std::logic_error("Node::getWallets() called in non-wallet build."); +#endif + } std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) override { return MakeHandler(::uiInterface.InitMessage.connect(fn)); diff --git a/src/interface/node.h b/src/interface/node.h index aeac1d6215..b0d435695e 100644 --- a/src/interface/node.h +++ b/src/interface/node.h @@ -6,6 +6,7 @@ #define BITCOIN_INTERFACE_NODE_H #include <addrdb.h> // For banmap_t +#include <amount.h> // For CAmount #include <init.h> // For HelpMessageMode #include <net.h> // For CConnman::NumConnections #include <netaddress.h> // For Network @@ -141,6 +142,12 @@ public: //! Get network active. virtual bool getNetworkActive() = 0; + //! Get tx confirm target. + virtual unsigned int getTxConfirmTarget() = 0; + + //! Get max tx fee. + virtual CAmount getMaxTxFee() = 0; + //! Execute rpc command. virtual UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) = 0; @@ -153,6 +160,9 @@ public: //! Unset RPC timer interface. virtual void rpcUnsetTimerInterface(RPCTimerInterface* iface) = 0; + //! Return interfaces for accessing wallets (if any). + virtual std::vector<std::unique_ptr<Wallet>> getWallets() = 0; + //! Register handler for init messages. using InitMessageFn = std::function<void(const std::string& message)>; virtual std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) = 0; diff --git a/src/interface/wallet.cpp b/src/interface/wallet.cpp index 01639aa37f..8dae7ac547 100644 --- a/src/interface/wallet.cpp +++ b/src/interface/wallet.cpp @@ -4,7 +4,21 @@ #include <interface/wallet.h> +#include <amount.h> +#include <chain.h> +#include <consensus/validation.h> #include <interface/handler.h> +#include <net.h> +#include <policy/policy.h> +#include <primitives/transaction.h> +#include <script/ismine.h> +#include <script/standard.h> +#include <support/allocators/secure.h> +#include <sync.h> +#include <ui_interface.h> +#include <uint256.h> +#include <validation.h> +#include <wallet/feebumper.h> #include <wallet/wallet.h> #include <memory> @@ -12,15 +26,213 @@ namespace interface { namespace { +class PendingWalletTxImpl : public PendingWalletTx +{ +public: + PendingWalletTxImpl(CWallet& wallet) : m_wallet(wallet), m_key(&wallet) {} + + const CTransaction& get() override { return *m_tx; } + + int64_t getVirtualSize() override { return GetVirtualTransactionSize(*m_tx); } + + bool commit(WalletValueMap value_map, + WalletOrderForm order_form, + std::string from_account, + std::string& reject_reason) override + { + LOCK2(cs_main, m_wallet.cs_wallet); + CValidationState state; + if (!m_wallet.CommitTransaction(m_tx, std::move(value_map), std::move(order_form), std::move(from_account), m_key, g_connman.get(), state)) { + reject_reason = state.GetRejectReason(); + return false; + } + return true; + } + + CTransactionRef m_tx; + CWallet& m_wallet; + CReserveKey m_key; +}; + class WalletImpl : public Wallet { public: WalletImpl(CWallet& wallet) : m_wallet(wallet) {} + bool encryptWallet(const SecureString& wallet_passphrase) override + { + return m_wallet.EncryptWallet(wallet_passphrase); + } + bool isCrypted() override { return m_wallet.IsCrypted(); } + bool lock() override { return m_wallet.Lock(); } + bool unlock(const SecureString& wallet_passphrase) override { return m_wallet.Unlock(wallet_passphrase); } + bool isLocked() override { return m_wallet.IsLocked(); } + bool changeWalletPassphrase(const SecureString& old_wallet_passphrase, + const SecureString& new_wallet_passphrase) override + { + return m_wallet.ChangeWalletPassphrase(old_wallet_passphrase, new_wallet_passphrase); + } + bool backupWallet(const std::string& filename) override { return m_wallet.BackupWallet(filename); } + std::string getWalletName() override { return m_wallet.GetName(); } + bool getPubKey(const CKeyID& address, CPubKey& pub_key) override { return m_wallet.GetPubKey(address, pub_key); } + bool getPrivKey(const CKeyID& address, CKey& key) override { return m_wallet.GetKey(address, key); } + bool isSpendable(const CTxDestination& dest) override { return IsMine(m_wallet, dest) & ISMINE_SPENDABLE; } + bool haveWatchOnly() override { return m_wallet.HaveWatchOnly(); }; + bool setAddressBook(const CTxDestination& dest, const std::string& name, const std::string& purpose) override + { + return m_wallet.SetAddressBook(dest, name, purpose); + } + bool getAddress(const CTxDestination& dest, std::string* name, isminetype* is_mine) override + { + LOCK(m_wallet.cs_wallet); + auto it = m_wallet.mapAddressBook.find(dest); + if (it == m_wallet.mapAddressBook.end()) { + return false; + } + if (name) { + *name = it->second.name; + } + if (is_mine) { + *is_mine = IsMine(m_wallet, dest); + } + return true; + } + bool addDestData(const CTxDestination& dest, const std::string& key, const std::string& value) override + { + LOCK(m_wallet.cs_wallet); + return m_wallet.AddDestData(dest, key, value); + } + bool eraseDestData(const CTxDestination& dest, const std::string& key) override + { + LOCK(m_wallet.cs_wallet); + return m_wallet.EraseDestData(dest, key); + } + std::vector<std::string> getDestValues(const std::string& prefix) override + { + return m_wallet.GetDestValues(prefix); + } + void lockCoin(const COutPoint& output) override + { + LOCK2(cs_main, m_wallet.cs_wallet); + return m_wallet.LockCoin(output); + } + void unlockCoin(const COutPoint& output) override + { + LOCK2(cs_main, m_wallet.cs_wallet); + return m_wallet.UnlockCoin(output); + } + bool isLockedCoin(const COutPoint& output) override + { + LOCK2(cs_main, m_wallet.cs_wallet); + return m_wallet.IsLockedCoin(output.hash, output.n); + } + void listLockedCoins(std::vector<COutPoint>& outputs) override + { + LOCK2(cs_main, m_wallet.cs_wallet); + return m_wallet.ListLockedCoins(outputs); + } + std::unique_ptr<PendingWalletTx> createTransaction(const std::vector<CRecipient>& recipients, + const CCoinControl& coin_control, + bool sign, + int& change_pos, + CAmount& fee, + std::string& fail_reason) override + { + LOCK2(cs_main, m_wallet.cs_wallet); + auto pending = MakeUnique<PendingWalletTxImpl>(m_wallet); + if (!m_wallet.CreateTransaction(recipients, pending->m_tx, pending->m_key, fee, change_pos, + fail_reason, coin_control, sign)) { + return {}; + } + return std::move(pending); + } + bool transactionCanBeAbandoned(const uint256& txid) override { return m_wallet.TransactionCanBeAbandoned(txid); } + bool abandonTransaction(const uint256& txid) override + { + LOCK2(cs_main, m_wallet.cs_wallet); + return m_wallet.AbandonTransaction(txid); + } + bool transactionCanBeBumped(const uint256& txid) override + { + return feebumper::TransactionCanBeBumped(&m_wallet, txid); + } + bool createBumpTransaction(const uint256& txid, + const CCoinControl& coin_control, + CAmount total_fee, + std::vector<std::string>& errors, + CAmount& old_fee, + CAmount& new_fee, + CMutableTransaction& mtx) override + { + return feebumper::CreateTransaction(&m_wallet, txid, coin_control, total_fee, errors, old_fee, new_fee, mtx) == + feebumper::Result::OK; + } + bool signBumpTransaction(CMutableTransaction& mtx) override { return feebumper::SignTransaction(&m_wallet, mtx); } + bool commitBumpTransaction(const uint256& txid, + CMutableTransaction&& mtx, + std::vector<std::string>& errors, + uint256& bumped_txid) override + { + return feebumper::CommitTransaction(&m_wallet, txid, std::move(mtx), errors, bumped_txid) == + feebumper::Result::OK; + } + WalletBalances getBalances() override + { + WalletBalances result; + result.balance = m_wallet.GetBalance(); + result.unconfirmed_balance = m_wallet.GetUnconfirmedBalance(); + result.immature_balance = m_wallet.GetImmatureBalance(); + result.have_watch_only = m_wallet.HaveWatchOnly(); + if (result.have_watch_only) { + result.watch_only_balance = m_wallet.GetWatchOnlyBalance(); + result.unconfirmed_watch_only_balance = m_wallet.GetUnconfirmedWatchOnlyBalance(); + result.immature_watch_only_balance = m_wallet.GetImmatureWatchOnlyBalance(); + } + return result; + } + bool tryGetBalances(WalletBalances& balances, int& num_blocks) override + { + TRY_LOCK(cs_main, locked_chain); + if (!locked_chain) return false; + TRY_LOCK(m_wallet.cs_wallet, locked_wallet); + if (!locked_wallet) { + return false; + } + balances = getBalances(); + num_blocks = ::chainActive.Height(); + return true; + } + CAmount getBalance() override { return m_wallet.GetBalance(); } + CAmount getAvailableBalance(const CCoinControl& coin_control) override + { + return m_wallet.GetAvailableBalance(&coin_control); + } + bool hdEnabled() override { return m_wallet.IsHDEnabled(); } + OutputType getDefaultAddressType() override { return m_wallet.m_default_address_type; } + OutputType getDefaultChangeType() override { return m_wallet.m_default_change_type; } std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) override { return MakeHandler(m_wallet.ShowProgress.connect(fn)); } + std::unique_ptr<Handler> handleStatusChanged(StatusChangedFn fn) override + { + return MakeHandler(m_wallet.NotifyStatusChanged.connect([fn](CCryptoKeyStore*) { fn(); })); + } + std::unique_ptr<Handler> handleAddressBookChanged(AddressBookChangedFn fn) override + { + return MakeHandler(m_wallet.NotifyAddressBookChanged.connect( + [fn](CWallet*, const CTxDestination& address, const std::string& label, bool is_mine, + const std::string& purpose, ChangeType status) { fn(address, label, is_mine, purpose, status); })); + } + std::unique_ptr<Handler> handleTransactionChanged(TransactionChangedFn fn) override + { + return MakeHandler(m_wallet.NotifyTransactionChanged.connect( + [fn, this](CWallet*, const uint256& txid, ChangeType status) { fn(txid, status); })); + } + std::unique_ptr<Handler> handleWatchOnlyChanged(WatchOnlyChangedFn fn) override + { + return MakeHandler(m_wallet.NotifyWatchonlyChanged.connect(fn)); + } CWallet& m_wallet; }; diff --git a/src/interface/wallet.h b/src/interface/wallet.h index bf68df4e7d..317b5c683c 100644 --- a/src/interface/wallet.h +++ b/src/interface/wallet.h @@ -5,15 +5,34 @@ #ifndef BITCOIN_INTERFACE_WALLET_H #define BITCOIN_INTERFACE_WALLET_H +#include <amount.h> // For CAmount +#include <script/ismine.h> // For isminefilter, isminetype +#include <script/standard.h> // For CTxDestination +#include <support/allocators/secure.h> // For SecureString +#include <ui_interface.h> // For ChangeType + #include <functional> +#include <map> #include <memory> +#include <stdint.h> #include <string> +#include <utility> +#include <vector> +class CCoinControl; +class CKey; class CWallet; +enum class OutputType; +struct CRecipient; namespace interface { class Handler; +class PendingWalletTx; +struct WalletBalances; + +using WalletOrderForm = std::vector<std::pair<std::string, std::string>>; +using WalletValueMap = std::map<std::string, std::string>; //! Interface for accessing a wallet. class Wallet @@ -21,9 +40,190 @@ class Wallet public: virtual ~Wallet() {} + //! Encrypt wallet. + virtual bool encryptWallet(const SecureString& wallet_passphrase) = 0; + + //! Return whether wallet is encrypted. + virtual bool isCrypted() = 0; + + //! Lock wallet. + virtual bool lock() = 0; + + //! Unlock wallet. + virtual bool unlock(const SecureString& wallet_passphrase) = 0; + + //! Return whether wallet is locked. + virtual bool isLocked() = 0; + + //! Change wallet passphrase. + virtual bool changeWalletPassphrase(const SecureString& old_wallet_passphrase, + const SecureString& new_wallet_passphrase) = 0; + + //! Back up wallet. + virtual bool backupWallet(const std::string& filename) = 0; + + //! Get wallet name. + virtual std::string getWalletName() = 0; + + //! Get public key. + virtual bool getPubKey(const CKeyID& address, CPubKey& pub_key) = 0; + + //! Get private key. + virtual bool getPrivKey(const CKeyID& address, CKey& key) = 0; + + //! Return whether wallet has private key. + virtual bool isSpendable(const CTxDestination& dest) = 0; + + //! Return whether wallet has watch only keys. + virtual bool haveWatchOnly() = 0; + + //! Add or update address. + virtual bool setAddressBook(const CTxDestination& dest, const std::string& name, const std::string& purpose) = 0; + + //! Look up address in wallet, return whether exists. + virtual bool getAddress(const CTxDestination& dest, + std::string* name = nullptr, + isminetype* is_mine = nullptr) = 0; + + //! Add dest data. + virtual bool addDestData(const CTxDestination& dest, const std::string& key, const std::string& value) = 0; + + //! Erase dest data. + virtual bool eraseDestData(const CTxDestination& dest, const std::string& key) = 0; + + //! Get dest values with prefix. + virtual std::vector<std::string> getDestValues(const std::string& prefix) = 0; + + //! Lock coin. + virtual void lockCoin(const COutPoint& output) = 0; + + //! Unlock coin. + virtual void unlockCoin(const COutPoint& output) = 0; + + //! Return whether coin is locked. + virtual bool isLockedCoin(const COutPoint& output) = 0; + + //! List locked coins. + virtual void listLockedCoins(std::vector<COutPoint>& outputs) = 0; + + //! Create transaction. + virtual std::unique_ptr<PendingWalletTx> createTransaction(const std::vector<CRecipient>& recipients, + const CCoinControl& coin_control, + bool sign, + int& change_pos, + CAmount& fee, + std::string& fail_reason) = 0; + + //! Return whether transaction can be abandoned. + virtual bool transactionCanBeAbandoned(const uint256& txid) = 0; + + //! Abandon transaction. + virtual bool abandonTransaction(const uint256& txid) = 0; + + //! Return whether transaction can be bumped. + virtual bool transactionCanBeBumped(const uint256& txid) = 0; + + //! Create bump transaction. + virtual bool createBumpTransaction(const uint256& txid, + const CCoinControl& coin_control, + CAmount total_fee, + std::vector<std::string>& errors, + CAmount& old_fee, + CAmount& new_fee, + CMutableTransaction& mtx) = 0; + + //! Sign bump transaction. + virtual bool signBumpTransaction(CMutableTransaction& mtx) = 0; + + //! Commit bump transaction. + virtual bool commitBumpTransaction(const uint256& txid, + CMutableTransaction&& mtx, + std::vector<std::string>& errors, + uint256& bumped_txid) = 0; + + //! Get balances. + virtual WalletBalances getBalances() = 0; + + //! Get balances if possible without blocking. + virtual bool tryGetBalances(WalletBalances& balances, int& num_blocks) = 0; + + //! Get balance. + virtual CAmount getBalance() = 0; + + //! Get available balance. + virtual CAmount getAvailableBalance(const CCoinControl& coin_control) = 0; + + // Return whether HD enabled. + virtual bool hdEnabled() = 0; + + // Get default address type. + virtual OutputType getDefaultAddressType() = 0; + + // Get default change type. + virtual OutputType getDefaultChangeType() = 0; + //! Register handler for show progress messages. using ShowProgressFn = std::function<void(const std::string& title, int progress)>; virtual std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) = 0; + + //! Register handler for status changed messages. + using StatusChangedFn = std::function<void()>; + virtual std::unique_ptr<Handler> handleStatusChanged(StatusChangedFn fn) = 0; + + //! Register handler for address book changed messages. + using AddressBookChangedFn = std::function<void(const CTxDestination& address, + const std::string& label, + bool is_mine, + const std::string& purpose, + ChangeType status)>; + virtual std::unique_ptr<Handler> handleAddressBookChanged(AddressBookChangedFn fn) = 0; + + //! Register handler for transaction changed messages. + using TransactionChangedFn = std::function<void(const uint256& txid, ChangeType status)>; + virtual std::unique_ptr<Handler> handleTransactionChanged(TransactionChangedFn fn) = 0; + + //! Register handler for watchonly changed messages. + using WatchOnlyChangedFn = std::function<void(bool have_watch_only)>; + virtual std::unique_ptr<Handler> handleWatchOnlyChanged(WatchOnlyChangedFn fn) = 0; +}; + +//! Tracking object returned by CreateTransaction and passed to CommitTransaction. +class PendingWalletTx +{ +public: + virtual ~PendingWalletTx() {} + + //! Get transaction data. + virtual const CTransaction& get() = 0; + + //! Get virtual transaction size. + virtual int64_t getVirtualSize() = 0; + + //! Send pending transaction and commit to wallet. + virtual bool commit(WalletValueMap value_map, + WalletOrderForm order_form, + std::string from_account, + std::string& reject_reason) = 0; +}; + +//! Collection of wallet balances. +struct WalletBalances +{ + CAmount balance = 0; + CAmount unconfirmed_balance = 0; + CAmount immature_balance = 0; + bool have_watch_only = false; + CAmount watch_only_balance = 0; + CAmount unconfirmed_watch_only_balance = 0; + CAmount immature_watch_only_balance = 0; + + bool balanceChanged(const WalletBalances& prev) const + { + return balance != prev.balance || unconfirmed_balance != prev.unconfirmed_balance || + immature_balance != prev.immature_balance || watch_only_balance != prev.watch_only_balance || + unconfirmed_watch_only_balance != prev.unconfirmed_watch_only_balance || + immature_watch_only_balance != prev.immature_watch_only_balance; + } }; //! Return implementation of Wallet interface. This function will be undefined diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 279726d6df..33a5fe8015 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -465,8 +465,10 @@ void BitcoinApplication::initializeResult(bool success) #ifdef ENABLE_WALLET bool fFirstWallet = true; - for (CWalletRef pwallet : vpwallets) { - WalletModel * const walletModel = new WalletModel(platformStyle, pwallet, optionsModel); + auto wallets = m_node.getWallets(); + auto cwallet = ::vpwallets.begin(); + for (auto& wallet : wallets) { + WalletModel * const walletModel = new WalletModel(std::move(wallet), m_node, platformStyle, *cwallet++, optionsModel); window->addWallet(walletModel); if (fFirstWallet) { diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index aa9f1f0071..63a2a200b2 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -1010,7 +1010,7 @@ void BitcoinGUI::incomingTransaction(const QString& date, int unit, const CAmoun // On new transaction, make an info balloon QString msg = tr("Date: %1\n").arg(date) + tr("Amount: %1\n").arg(BitcoinUnits::formatWithUnit(unit, amount, true)); - if (WalletModel::isMultiwallet() && !walletName.isEmpty()) { + if (m_node.getWallets().size() > 1 && !walletName.isEmpty()) { msg += tr("Wallet: %1\n").arg(walletName); } msg += tr("Type: %1\n").arg(type); @@ -1116,7 +1116,7 @@ void BitcoinGUI::updateWalletStatus() } WalletModel * const walletModel = walletView->getWalletModel(); setEncryptionStatus(walletModel->getEncryptionStatus()); - setHDStatus(walletModel->hdEnabled()); + setHDStatus(walletModel->wallet().hdEnabled()); } #endif // ENABLE_WALLET diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index a45e9f85c1..0a656a6f9b 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -209,7 +209,7 @@ void CoinControlDialog::showMenu(const QPoint &point) if (item->text(COLUMN_TXHASH).length() == 64) // transaction hash is 64 characters (this means it is a child node, so it is not a parent node in tree mode) { copyTransactionHashAction->setEnabled(true); - if (model->isLockedCoin(uint256S(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt())) + if (model->wallet().isLockedCoin(COutPoint(uint256S(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt()))) { lockAction->setEnabled(false); unlockAction->setEnabled(true); @@ -269,7 +269,7 @@ void CoinControlDialog::lockCoin() contextMenuItem->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); COutPoint outpt(uint256S(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt()); - model->lockCoin(outpt); + model->wallet().lockCoin(outpt); contextMenuItem->setDisabled(true); contextMenuItem->setIcon(COLUMN_CHECKBOX, platformStyle->SingleColorIcon(":/icons/lock_closed")); updateLabelLocked(); @@ -279,7 +279,7 @@ void CoinControlDialog::lockCoin() void CoinControlDialog::unlockCoin() { COutPoint outpt(uint256S(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt()); - model->unlockCoin(outpt); + model->wallet().unlockCoin(outpt); contextMenuItem->setDisabled(false); contextMenuItem->setIcon(COLUMN_CHECKBOX, QIcon()); updateLabelLocked(); @@ -405,7 +405,7 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column) void CoinControlDialog::updateLabelLocked() { std::vector<COutPoint> vOutpts; - model->listLockedCoins(vOutpts); + model->wallet().listLockedCoins(vOutpts); if (vOutpts.size() > 0) { ui->labelLocked->setText(tr("(%1 locked)").arg(vOutpts.size())); @@ -479,7 +479,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) { CPubKey pubkey; CKeyID *keyid = boost::get<CKeyID>(&address); - if (keyid && model->getPubKey(*keyid, pubkey)) + if (keyid && model->wallet().getPubKey(*keyid, pubkey)) { nBytesInputs += (pubkey.IsCompressed() ? 148 : 180); } @@ -706,7 +706,7 @@ void CoinControlDialog::updateView() itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(out.i)); // disable locked coins - if (model->isLockedCoin(txhash, out.i)) + if (model->wallet().isLockedCoin(COutPoint(txhash, out.i))) { COutPoint outpt(txhash, out.i); coinControl()->UnSelect(outpt); // just to be sure diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index d1c9f17961..aaea650141 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -231,13 +231,15 @@ void OverviewPage::setWalletModel(WalletModel *model) ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress); // Keep up to date with wallet - setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance(), - model->getWatchBalance(), model->getWatchUnconfirmedBalance(), model->getWatchImmatureBalance()); + interface::Wallet& wallet = model->wallet(); + interface::WalletBalances balances = wallet.getBalances(); + setBalance(balances.balance, balances.unconfirmed_balance, balances.immature_balance, + balances.watch_only_balance, balances.unconfirmed_watch_only_balance, balances.immature_watch_only_balance); connect(model, SIGNAL(balanceChanged(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)), this, SLOT(setBalance(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount))); connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); - updateWatchOnlyLabels(model->haveWatchOnly()); + updateWatchOnlyLabels(wallet.haveWatchOnly()); connect(model, SIGNAL(notifyWatchonlyChanged(bool)), this, SLOT(updateWatchOnlyLabels(bool))); } diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp index c8b6366db0..70e11f0296 100644 --- a/src/qt/receivecoinsdialog.cpp +++ b/src/qt/receivecoinsdialog.cpp @@ -95,13 +95,13 @@ void ReceiveCoinsDialog::setModel(WalletModel *_model) columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(tableView, AMOUNT_MINIMUM_COLUMN_WIDTH, DATE_COLUMN_WIDTH, this); // configure bech32 checkbox, disable if launched with legacy as default: - if (model->getDefaultAddressType() == OutputType::BECH32) { + if (model->wallet().getDefaultAddressType() == OutputType::BECH32) { ui->useBech32->setCheckState(Qt::Checked); } else { ui->useBech32->setCheckState(Qt::Unchecked); } - ui->useBech32->setVisible(model->getDefaultAddressType() != OutputType::LEGACY); + ui->useBech32->setVisible(model->wallet().getDefaultAddressType() != OutputType::LEGACY); } } @@ -144,7 +144,7 @@ void ReceiveCoinsDialog::on_receiveButton_clicked() QString address; QString label = ui->reqLabel->text(); /* Generate new receiving address */ - OutputType address_type = model->getDefaultAddressType(); + OutputType address_type = model->wallet().getDefaultAddressType(); if (address_type != OutputType::LEGACY) { address_type = ui->useBech32->isChecked() ? OutputType::BECH32 : OutputType::P2SH_SEGWIT; } diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 8a52aadbb0..98f08bbc59 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -15,6 +15,7 @@ #include <qt/sendcoinsentry.h> #include <chainparams.h> +#include <interface/node.h> #include <key_io.h> #include <wallet/coincontrol.h> #include <validation.h> // mempool and minRelayTxFee @@ -149,8 +150,9 @@ void SendCoinsDialog::setModel(WalletModel *_model) } } - setBalance(_model->getBalance(), _model->getUnconfirmedBalance(), _model->getImmatureBalance(), - _model->getWatchBalance(), _model->getWatchUnconfirmedBalance(), _model->getWatchImmatureBalance()); + interface::WalletBalances balances = _model->wallet().getBalances(); + setBalance(balances.balance, balances.unconfirmed_balance, balances.immature_balance, + balances.watch_only_balance, balances.unconfirmed_watch_only_balance, balances.immature_watch_only_balance); connect(_model, SIGNAL(balanceChanged(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)), this, SLOT(setBalance(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount))); connect(_model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); updateDisplayUnit(); @@ -193,7 +195,7 @@ void SendCoinsDialog::setModel(WalletModel *_model) settings.remove("nSmartFeeSliderPosition"); } if (settings.value("nConfTarget").toInt() == 0) - ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(model->getDefaultConfirmTarget())); + ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(model->node().getTxConfirmTarget())); else ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(settings.value("nConfTarget").toInt())); } @@ -372,7 +374,7 @@ void SendCoinsDialog::on_sendButton_clicked() accept(); CoinControlDialog::coinControl()->UnSelectAll(); coinControlUpdateLabels(); - Q_EMIT coinsSent(currentTransaction.getTransaction()->GetHash()); + Q_EMIT coinsSent(currentTransaction.getWtx()->get().GetHash()); } fNewRecipientAllowed = true; } @@ -532,7 +534,7 @@ void SendCoinsDialog::setBalance(const CAmount& balance, const CAmount& unconfir void SendCoinsDialog::updateDisplayUnit() { - setBalance(model->getBalance(), 0, 0, 0, 0, 0); + setBalance(model->wallet().getBalance(), 0, 0, 0, 0, 0); ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit()); updateMinFeeLabel(); updateSmartFeeLabel(); @@ -618,7 +620,7 @@ void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry) } // Calculate available amount to send. - CAmount amount = model->getBalance(&coin_control); + CAmount amount = model->wallet().getAvailableBalance(coin_control); for (int i = 0; i < ui->entries->count(); ++i) { SendCoinsEntry* e = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget()); if (e && !e->isHidden() && e != entry) { @@ -814,7 +816,7 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text) } else // Valid address { - if (!model->IsSpendable(dest)) { + if (!model->wallet().isSpendable(dest)) { ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address")); // confirmation dialog diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index 8dade8df79..94a3ad7987 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -140,7 +140,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked() } CKey key; - if (!model->getPrivKey(*keyID, key)) + if (!model->wallet().getPrivKey(*keyID, key)) { ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_SM->setText(tr("Private key for the entered address is not available.")); diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index ebca4b6499..d28d4bd7e6 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -178,7 +178,9 @@ void TestGUI() TransactionView transactionView(platformStyle.get()); auto node = interface::MakeNode(); OptionsModel optionsModel(*node); - WalletModel walletModel(platformStyle.get(), &wallet, &optionsModel); + vpwallets.insert(vpwallets.begin(), &wallet); + WalletModel walletModel(std::move(node->getWallets()[0]), *node, platformStyle.get(), &wallet, &optionsModel); + vpwallets.erase(vpwallets.begin()); sendCoinsDialog.setModel(&walletModel); transactionView.setModel(&walletModel); @@ -203,7 +205,7 @@ void TestGUI() QLabel* balanceLabel = overviewPage.findChild<QLabel*>("labelBalance"); QString balanceText = balanceLabel->text(); int unit = walletModel.getOptionsModel()->getDisplayUnit(); - CAmount balance = walletModel.getBalance(); + CAmount balance = walletModel.wallet().getBalance(); QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways); QCOMPARE(balanceText, balanceComparison); diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 26391452da..aa6444245a 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -254,7 +254,7 @@ void TransactionView::setModel(WalletModel *_model) } // show/hide column Watch-only - updateWatchOnlyColumn(_model->haveWatchOnly()); + updateWatchOnlyColumn(_model->wallet().haveWatchOnly()); // Watch-only signal connect(_model, SIGNAL(notifyWatchonlyChanged(bool)), this, SLOT(updateWatchOnlyColumn(bool))); @@ -364,7 +364,7 @@ void TransactionView::exportClicked() // name, column, role writer.setModel(transactionProxyModel); writer.addColumn(tr("Confirmed"), 0, TransactionTableModel::ConfirmedRole); - if (model->haveWatchOnly()) + if (model->wallet().haveWatchOnly()) writer.addColumn(tr("Watch-only"), TransactionTableModel::Watchonly); writer.addColumn(tr("Date"), 0, TransactionTableModel::DateRole); writer.addColumn(tr("Type"), TransactionTableModel::Type, Qt::EditRole); @@ -393,8 +393,8 @@ void TransactionView::contextualMenu(const QPoint &point) // check if transaction can be abandoned, disable context menu action in case it doesn't uint256 hash; hash.SetHex(selection.at(0).data(TransactionTableModel::TxHashRole).toString().toStdString()); - abandonAction->setEnabled(model->transactionCanBeAbandoned(hash)); - bumpFeeAction->setEnabled(model->transactionCanBeBumped(hash)); + abandonAction->setEnabled(model->wallet().transactionCanBeAbandoned(hash)); + bumpFeeAction->setEnabled(model->wallet().transactionCanBeBumped(hash)); if(index.isValid()) { @@ -414,7 +414,7 @@ void TransactionView::abandonTx() hash.SetHex(hashQStr.toStdString()); // Abandon the wallet transaction over the walletModel - model->abandonTransaction(hash); + model->wallet().abandonTransaction(hash); // Update the table model->getTransactionTableModel()->updateTransaction(hashQStr, CT_UPDATED, false); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 795302be58..7a19773cf7 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -15,6 +15,8 @@ #include <qt/transactiontablemodel.h> #include <chain.h> +#include <interface/handler.h> +#include <interface/node.h> #include <key_io.h> #include <keystore.h> #include <validation.h> @@ -37,21 +39,19 @@ #include <QTimer> -WalletModel::WalletModel(const PlatformStyle *platformStyle, CWallet *_wallet, OptionsModel *_optionsModel, QObject *parent) : - QObject(parent), wallet(_wallet), optionsModel(_optionsModel), addressTableModel(0), +WalletModel::WalletModel(std::unique_ptr<interface::Wallet> wallet, interface::Node& node, const PlatformStyle *platformStyle, CWallet *_wallet, OptionsModel *_optionsModel, QObject *parent) : + QObject(parent), m_wallet(std::move(wallet)), m_node(node), cwallet(_wallet), optionsModel(_optionsModel), addressTableModel(0), transactionTableModel(0), recentRequestsTableModel(0), - cachedBalance(0), cachedUnconfirmedBalance(0), cachedImmatureBalance(0), - cachedWatchOnlyBalance{0}, cachedWatchUnconfBalance{0}, cachedWatchImmatureBalance{0}, cachedEncryptionStatus(Unencrypted), cachedNumBlocks(0) { - fHaveWatchOnly = wallet->HaveWatchOnly(); + fHaveWatchOnly = m_wallet->haveWatchOnly(); fForceCheckBalanceChanged = false; - addressTableModel = new AddressTableModel(wallet, this); - transactionTableModel = new TransactionTableModel(platformStyle, wallet, this); - recentRequestsTableModel = new RecentRequestsTableModel(wallet, this); + addressTableModel = new AddressTableModel(cwallet, this); + transactionTableModel = new TransactionTableModel(platformStyle, cwallet, this); + recentRequestsTableModel = new RecentRequestsTableModel(cwallet, this); // This timer will be fired repeatedly to update the balance pollTimer = new QTimer(this); @@ -66,46 +66,6 @@ WalletModel::~WalletModel() unsubscribeFromCoreSignals(); } -CAmount WalletModel::getBalance(const CCoinControl *coinControl) const -{ - if (coinControl) - { - return wallet->GetAvailableBalance(coinControl); - } - - return wallet->GetBalance(); -} - -CAmount WalletModel::getUnconfirmedBalance() const -{ - return wallet->GetUnconfirmedBalance(); -} - -CAmount WalletModel::getImmatureBalance() const -{ - return wallet->GetImmatureBalance(); -} - -bool WalletModel::haveWatchOnly() const -{ - return fHaveWatchOnly; -} - -CAmount WalletModel::getWatchBalance() const -{ - return wallet->GetWatchOnlyBalance(); -} - -CAmount WalletModel::getWatchUnconfirmedBalance() const -{ - return wallet->GetUnconfirmedWatchOnlyBalance(); -} - -CAmount WalletModel::getWatchImmatureBalance() const -{ - return wallet->GetImmatureWatchOnlyBalance(); -} - void WalletModel::updateStatus() { EncryptionStatus newEncryptionStatus = getEncryptionStatus(); @@ -117,55 +77,34 @@ void WalletModel::updateStatus() void WalletModel::pollBalanceChanged() { - // Get required locks upfront. This avoids the GUI from getting stuck on - // periodical polls if the core is holding the locks for a longer time - - // for example, during a wallet rescan. - TRY_LOCK(cs_main, lockMain); - if(!lockMain) - return; - TRY_LOCK(wallet->cs_wallet, lockWallet); - if(!lockWallet) + // Try to get balances and return early if locks can't be acquired. This + // avoids the GUI from getting stuck on periodical polls if the core is + // holding the locks for a longer time - for example, during a wallet + // rescan. + interface::WalletBalances new_balances; + int numBlocks = -1; + if (!m_wallet->tryGetBalances(new_balances, numBlocks)) { return; + } - if(fForceCheckBalanceChanged || chainActive.Height() != cachedNumBlocks) + if(fForceCheckBalanceChanged || m_node.getNumBlocks() != cachedNumBlocks) { fForceCheckBalanceChanged = false; // Balance and number of transactions might have changed - cachedNumBlocks = chainActive.Height(); + cachedNumBlocks = m_node.getNumBlocks(); - checkBalanceChanged(); + checkBalanceChanged(new_balances); if(transactionTableModel) transactionTableModel->updateConfirmations(); } } -void WalletModel::checkBalanceChanged() +void WalletModel::checkBalanceChanged(const interface::WalletBalances& new_balances) { - CAmount newBalance = getBalance(); - CAmount newUnconfirmedBalance = getUnconfirmedBalance(); - CAmount newImmatureBalance = getImmatureBalance(); - CAmount newWatchOnlyBalance = 0; - CAmount newWatchUnconfBalance = 0; - CAmount newWatchImmatureBalance = 0; - if (haveWatchOnly()) - { - newWatchOnlyBalance = getWatchBalance(); - newWatchUnconfBalance = getWatchUnconfirmedBalance(); - newWatchImmatureBalance = getWatchImmatureBalance(); - } - - if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance || - cachedWatchOnlyBalance != newWatchOnlyBalance || cachedWatchUnconfBalance != newWatchUnconfBalance || cachedWatchImmatureBalance != newWatchImmatureBalance) - { - cachedBalance = newBalance; - cachedUnconfirmedBalance = newUnconfirmedBalance; - cachedImmatureBalance = newImmatureBalance; - cachedWatchOnlyBalance = newWatchOnlyBalance; - cachedWatchUnconfBalance = newWatchUnconfBalance; - cachedWatchImmatureBalance = newWatchImmatureBalance; - Q_EMIT balanceChanged(newBalance, newUnconfirmedBalance, newImmatureBalance, - newWatchOnlyBalance, newWatchUnconfBalance, newWatchImmatureBalance); + if(new_balances.balanceChanged(m_cached_balances)) { + m_cached_balances = new_balances; + Q_EMIT balanceChanged(new_balances.balance, new_balances.unconfirmed_balance, new_balances.immature_balance, new_balances.watch_only_balance, new_balances.unconfirmed_watch_only_balance, new_balances.immature_watch_only_balance); } } @@ -260,7 +199,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact return DuplicateAddress; } - CAmount nBalance = getBalance(&coinControl); + CAmount nBalance = m_wallet->getAvailableBalance(coinControl); if(total > nBalance) { @@ -268,22 +207,17 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact } { - LOCK2(cs_main, wallet->cs_wallet); - - transaction.newPossibleKeyChange(wallet); - CAmount nFeeRequired = 0; int nChangePosRet = -1; std::string strFailReason; - CTransactionRef& newTx = transaction.getTransaction(); - CReserveKey *keyChange = transaction.getPossibleKeyChange(); - bool fCreated = wallet->CreateTransaction(vecSend, newTx, *keyChange, nFeeRequired, nChangePosRet, strFailReason, coinControl); + auto& newTx = transaction.getWtx(); + newTx = m_wallet->createTransaction(vecSend, coinControl, true /* sign */, nChangePosRet, nFeeRequired, strFailReason); transaction.setTransactionFee(nFeeRequired); - if (fSubtractFeeFromAmount && fCreated) + if (fSubtractFeeFromAmount && newTx) transaction.reassignAmounts(nChangePosRet); - if(!fCreated) + if(!newTx) { if(!fSubtractFeeFromAmount && (total + nFeeRequired) > nBalance) { @@ -297,7 +231,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact // reject absurdly high fee. (This can never happen because the // wallet caps the fee at maxTxFee. This merely serves as a // belt-and-suspenders check) - if (nFeeRequired > maxTxFee) + if (nFeeRequired > m_node.getMaxTxFee()) return AbsurdFee; } @@ -309,8 +243,6 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran QByteArray transaction_array; /* store serialized transaction */ { - LOCK2(cs_main, wallet->cs_wallet); - std::vector<std::pair<std::string, std::string>> vOrderForm; for (const SendCoinsRecipient &rcp : transaction.getRecipients()) { @@ -330,14 +262,13 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran vOrderForm.emplace_back("Message", rcp.message.toStdString()); } - CTransactionRef& newTx = transaction.getTransaction(); - CReserveKey *keyChange = transaction.getPossibleKeyChange(); - CValidationState state; - if (!wallet->CommitTransaction(newTx, {} /* mapValue */, std::move(vOrderForm), {} /* fromAccount */, *keyChange, g_connman.get(), state)) - return SendCoinsReturn(TransactionCommitFailed, QString::fromStdString(state.GetRejectReason())); + auto& newTx = transaction.getWtx(); + std::string rejectReason; + if (!newTx->commit({} /* mapValue */, std::move(vOrderForm), {} /* fromAccount */, rejectReason)) + return SendCoinsReturn(TransactionCommitFailed, QString::fromStdString(rejectReason)); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); - ssTx << newTx; + ssTx << newTx->get(); transaction_array.append(&(ssTx[0]), ssTx.size()); } @@ -352,24 +283,22 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran CTxDestination dest = DecodeDestination(strAddress); std::string strLabel = rcp.label.toStdString(); { - LOCK(wallet->cs_wallet); - - std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(dest); - // Check if we have a new address or an updated label - if (mi == wallet->mapAddressBook.end()) + std::string name; + if (!m_wallet->getAddress(dest, &name)) { - wallet->SetAddressBook(dest, strLabel, "send"); + m_wallet->setAddressBook(dest, strLabel, "send"); } - else if (mi->second.name != strLabel) + else if (name != strLabel) { - wallet->SetAddressBook(dest, strLabel, ""); // "" means don't change purpose + m_wallet->setAddressBook(dest, strLabel, ""); // "" means don't change purpose } } } - Q_EMIT coinsSent(wallet, rcp, transaction_array); + Q_EMIT coinsSent(cwallet, rcp, transaction_array); } - checkBalanceChanged(); // update balance immediately, otherwise there could be a short noticeable delay until pollBalanceChanged hits + + checkBalanceChanged(m_wallet->getBalances()); // update balance immediately, otherwise there could be a short noticeable delay until pollBalanceChanged hits return SendCoinsReturn(OK); } @@ -396,11 +325,11 @@ RecentRequestsTableModel *WalletModel::getRecentRequestsTableModel() WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const { - if(!wallet->IsCrypted()) + if(!m_wallet->isCrypted()) { return Unencrypted; } - else if(wallet->IsLocked()) + else if(m_wallet->isLocked()) { return Locked; } @@ -415,7 +344,7 @@ bool WalletModel::setWalletEncrypted(bool encrypted, const SecureString &passphr if(encrypted) { // Encrypt - return wallet->EncryptWallet(passphrase); + return m_wallet->encryptWallet(passphrase); } else { @@ -429,39 +358,29 @@ bool WalletModel::setWalletLocked(bool locked, const SecureString &passPhrase) if(locked) { // Lock - return wallet->Lock(); + return m_wallet->lock(); } else { // Unlock - return wallet->Unlock(passPhrase); + return m_wallet->unlock(passPhrase); } } bool WalletModel::changePassphrase(const SecureString &oldPass, const SecureString &newPass) { - bool retval; - { - LOCK(wallet->cs_wallet); - wallet->Lock(); // Make sure wallet is locked before attempting pass change - retval = wallet->ChangeWalletPassphrase(oldPass, newPass); - } - return retval; -} - -bool WalletModel::backupWallet(const QString &filename) -{ - return wallet->BackupWallet(filename.toLocal8Bit().data()); + m_wallet->lock(); // Make sure wallet is locked before attempting pass change + return m_wallet->changeWalletPassphrase(oldPass, newPass); } // Handlers for core signals -static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel, CCryptoKeyStore *wallet) +static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel) { qDebug() << "NotifyKeyStoreStatusChanged"; QMetaObject::invokeMethod(walletmodel, "updateStatus", Qt::QueuedConnection); } -static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet, +static void NotifyAddressBookChanged(WalletModel *walletmodel, const CTxDestination &address, const std::string &label, bool isMine, const std::string &purpose, ChangeType status) { @@ -478,9 +397,8 @@ static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet, Q_ARG(int, status)); } -static void NotifyTransactionChanged(WalletModel *walletmodel, CWallet *wallet, const uint256 &hash, ChangeType status) +static void NotifyTransactionChanged(WalletModel *walletmodel, const uint256 &hash, ChangeType status) { - Q_UNUSED(wallet); Q_UNUSED(hash); Q_UNUSED(status); QMetaObject::invokeMethod(walletmodel, "updateTransaction", Qt::QueuedConnection); @@ -503,21 +421,21 @@ static void NotifyWatchonlyChanged(WalletModel *walletmodel, bool fHaveWatchonly void WalletModel::subscribeToCoreSignals() { // Connect signals to wallet - wallet->NotifyStatusChanged.connect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1)); - wallet->NotifyAddressBookChanged.connect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5, _6)); - wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); - wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2)); - wallet->NotifyWatchonlyChanged.connect(boost::bind(NotifyWatchonlyChanged, this, _1)); + m_handler_status_changed = m_wallet->handleStatusChanged(boost::bind(&NotifyKeyStoreStatusChanged, this)); + m_handler_address_book_changed = m_wallet->handleAddressBookChanged(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5)); + m_handler_transaction_changed = m_wallet->handleTransactionChanged(boost::bind(NotifyTransactionChanged, this, _1, _2)); + m_handler_show_progress = m_wallet->handleShowProgress(boost::bind(ShowProgress, this, _1, _2)); + m_handler_watch_only_changed = m_wallet->handleWatchOnlyChanged(boost::bind(NotifyWatchonlyChanged, this, _1)); } void WalletModel::unsubscribeFromCoreSignals() { // Disconnect signals from wallet - wallet->NotifyStatusChanged.disconnect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1)); - wallet->NotifyAddressBookChanged.disconnect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5, _6)); - wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); - wallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2)); - wallet->NotifyWatchonlyChanged.disconnect(boost::bind(NotifyWatchonlyChanged, this, _1)); + m_handler_status_changed->disconnect(); + m_handler_address_book_changed->disconnect(); + m_handler_transaction_changed->disconnect(); + m_handler_show_progress->disconnect(); + m_handler_watch_only_changed->disconnect(); } // WalletModel::UnlockContext implementation @@ -557,29 +475,14 @@ void WalletModel::UnlockContext::CopyFrom(const UnlockContext& rhs) rhs.relock = false; } -bool WalletModel::getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const -{ - return wallet->GetPubKey(address, vchPubKeyOut); -} - -bool WalletModel::IsSpendable(const CTxDestination& dest) const -{ - return IsMine(*wallet, dest) & ISMINE_SPENDABLE; -} - -bool WalletModel::getPrivKey(const CKeyID &address, CKey& vchPrivKeyOut) const -{ - return wallet->GetKey(address, vchPrivKeyOut); -} - // returns a list of COutputs from COutPoints void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vector<COutput>& vOutputs) { - LOCK2(cs_main, wallet->cs_wallet); + LOCK2(cs_main, cwallet->cs_wallet); for (const COutPoint& outpoint : vOutpoints) { - auto it = wallet->mapWallet.find(outpoint.hash); - if (it == wallet->mapWallet.end()) continue; + auto it = cwallet->mapWallet.find(outpoint.hash); + if (it == cwallet->mapWallet.end()) continue; int nDepth = it->second.GetDepthInMainChain(); if (nDepth < 0) continue; COutput out(&it->second, outpoint.n, nDepth, true /* spendable */, true /* solvable */, true /* safe */); @@ -589,14 +492,14 @@ void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vect bool WalletModel::isSpent(const COutPoint& outpoint) const { - LOCK2(cs_main, wallet->cs_wallet); - return wallet->IsSpent(outpoint.hash, outpoint.n); + LOCK2(cs_main, cwallet->cs_wallet); + return cwallet->IsSpent(outpoint.hash, outpoint.n); } // AvailableCoins + LockedCoins grouped by wallet address (put change in one group with wallet address) void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) const { - for (auto& group : wallet->ListCoins()) { + for (auto& group : cwallet->ListCoins()) { auto& resultGroup = mapCoins[QString::fromStdString(EncodeDestination(group.first))]; for (auto& coin : group.second) { resultGroup.emplace_back(std::move(coin)); @@ -604,33 +507,9 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) } } -bool WalletModel::isLockedCoin(uint256 hash, unsigned int n) const -{ - LOCK2(cs_main, wallet->cs_wallet); - return wallet->IsLockedCoin(hash, n); -} - -void WalletModel::lockCoin(COutPoint& output) -{ - LOCK2(cs_main, wallet->cs_wallet); - wallet->LockCoin(output); -} - -void WalletModel::unlockCoin(COutPoint& output) -{ - LOCK2(cs_main, wallet->cs_wallet); - wallet->UnlockCoin(output); -} - -void WalletModel::listLockedCoins(std::vector<COutPoint>& vOutpts) -{ - LOCK2(cs_main, wallet->cs_wallet); - wallet->ListLockedCoins(vOutpts); -} - void WalletModel::loadReceiveRequests(std::vector<std::string>& vReceiveRequests) { - vReceiveRequests = wallet->GetDestValues("rr"); // receive request + vReceiveRequests = m_wallet->getDestValues("rr"); // receive request } bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest) @@ -641,27 +520,10 @@ bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t ss << nId; std::string key = "rr" + ss.str(); // "rr" prefix = "receive request" in destdata - LOCK(wallet->cs_wallet); if (sRequest.empty()) - return wallet->EraseDestData(dest, key); + return m_wallet->eraseDestData(dest, key); else - return wallet->AddDestData(dest, key, sRequest); -} - -bool WalletModel::transactionCanBeAbandoned(uint256 hash) const -{ - return wallet->TransactionCanBeAbandoned(hash); -} - -bool WalletModel::abandonTransaction(uint256 hash) const -{ - LOCK2(cs_main, wallet->cs_wallet); - return wallet->AbandonTransaction(hash); -} - -bool WalletModel::transactionCanBeBumped(uint256 hash) const -{ - return feebumper::TransactionCanBeBumped(wallet, hash); + return m_wallet->addDestData(dest, key, sRequest); } bool WalletModel::bumpFee(uint256 hash) @@ -672,7 +534,7 @@ bool WalletModel::bumpFee(uint256 hash) CAmount old_fee; CAmount new_fee; CMutableTransaction mtx; - if (feebumper::CreateTransaction(wallet, hash, coin_control, 0 /* totalFee */, errors, old_fee, new_fee, mtx) != feebumper::Result::OK) { + if (!m_wallet->createBumpTransaction(hash, coin_control, 0 /* totalFee */, errors, old_fee, new_fee, mtx)) { QMessageBox::critical(0, tr("Fee bump error"), tr("Increasing transaction fee failed") + "<br />(" + (errors.size() ? QString::fromStdString(errors[0]) : "") +")"); return false; @@ -711,13 +573,13 @@ bool WalletModel::bumpFee(uint256 hash) } // sign bumped transaction - if (!feebumper::SignTransaction(wallet, mtx)) { + if (!m_wallet->signBumpTransaction(mtx)) { QMessageBox::critical(0, tr("Fee bump error"), tr("Can't sign transaction.")); return false; } // commit the bumped transaction uint256 txid; - if (feebumper::CommitTransaction(wallet, hash, std::move(mtx), errors, txid) != feebumper::Result::OK) { + if(!m_wallet->commitBumpTransaction(hash, std::move(mtx), errors, txid)) { QMessageBox::critical(0, tr("Fee bump error"), tr("Could not commit transaction") + "<br />(" + QString::fromStdString(errors[0])+")"); return false; @@ -730,28 +592,12 @@ bool WalletModel::isWalletEnabled() return !gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET); } -bool WalletModel::hdEnabled() const -{ - return wallet->IsHDEnabled(); -} - -OutputType WalletModel::getDefaultAddressType() const -{ - return wallet->m_default_address_type; -} - -int WalletModel::getDefaultConfirmTarget() const -{ - return nTxConfirmTarget; -} - QString WalletModel::getWalletName() const { - LOCK(wallet->cs_wallet); - return QString::fromStdString(wallet->GetName()); + return QString::fromStdString(m_wallet->getWalletName()); } bool WalletModel::isMultiwallet() { - return gArgs.GetArgs("-wallet").size() > 1; + return m_node.getWallets().size() > 1; } diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index ff4b38a804..8ff19f4ae5 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -13,6 +13,7 @@ #include <qt/paymentrequestplus.h> #include <qt/walletmodeltransaction.h> +#include <interface/wallet.h> #include <support/allocators/secure.h> #include <map> @@ -34,9 +35,12 @@ class CKeyID; class COutPoint; class COutput; class CPubKey; -class CWallet; class uint256; +namespace interface { +class Node; +} // namespace interface + QT_BEGIN_NAMESPACE class QTimer; QT_END_NAMESPACE @@ -107,7 +111,7 @@ class WalletModel : public QObject Q_OBJECT public: - explicit WalletModel(const PlatformStyle *platformStyle, CWallet *wallet, OptionsModel *optionsModel, QObject *parent = 0); + explicit WalletModel(std::unique_ptr<interface::Wallet> wallet, interface::Node& node, const PlatformStyle *platformStyle, CWallet *cwallet, OptionsModel *optionsModel, QObject *parent = 0); ~WalletModel(); enum StatusCode // Returned by sendCoins @@ -136,15 +140,6 @@ public: TransactionTableModel *getTransactionTableModel(); RecentRequestsTableModel *getRecentRequestsTableModel(); - CWallet *getWallet() const { return wallet; }; - - CAmount getBalance(const CCoinControl *coinControl = nullptr) const; - CAmount getUnconfirmedBalance() const; - CAmount getImmatureBalance() const; - bool haveWatchOnly() const; - CAmount getWatchBalance() const; - CAmount getWatchUnconfirmedBalance() const; - CAmount getWatchImmatureBalance() const; EncryptionStatus getEncryptionStatus() const; // Check address for validity @@ -173,8 +168,6 @@ public: // Passphrase only needed when unlocking bool setWalletLocked(bool locked, const SecureString &passPhrase=SecureString()); bool changePassphrase(const SecureString &oldPass, const SecureString &newPass); - // Wallet backup - bool backupWallet(const QString &filename); // RAI object for unlocking wallet, returned by requestUnlock() class UnlockContext @@ -198,40 +191,33 @@ public: UnlockContext requestUnlock(); - bool getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; - bool IsSpendable(const CTxDestination& dest) const; - bool getPrivKey(const CKeyID &address, CKey& vchPrivKeyOut) const; void getOutputs(const std::vector<COutPoint>& vOutpoints, std::vector<COutput>& vOutputs); bool isSpent(const COutPoint& outpoint) const; void listCoins(std::map<QString, std::vector<COutput> >& mapCoins) const; - bool isLockedCoin(uint256 hash, unsigned int n) const; - void lockCoin(COutPoint& output); - void unlockCoin(COutPoint& output); - void listLockedCoins(std::vector<COutPoint>& vOutpts); - void loadReceiveRequests(std::vector<std::string>& vReceiveRequests); bool saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest); - bool transactionCanBeAbandoned(uint256 hash) const; - bool abandonTransaction(uint256 hash) const; - - bool transactionCanBeBumped(uint256 hash) const; bool bumpFee(uint256 hash); static bool isWalletEnabled(); - bool hdEnabled() const; - - OutputType getDefaultAddressType() const; - - int getDefaultConfirmTarget() const; + interface::Node& node() const { return m_node; } + interface::Wallet& wallet() const { return *m_wallet; } QString getWalletName() const; - static bool isMultiwallet(); + bool isMultiwallet(); private: - CWallet *wallet; + std::unique_ptr<interface::Wallet> m_wallet; + std::unique_ptr<interface::Handler> m_handler_status_changed; + std::unique_ptr<interface::Handler> m_handler_address_book_changed; + std::unique_ptr<interface::Handler> m_handler_transaction_changed; + std::unique_ptr<interface::Handler> m_handler_show_progress; + std::unique_ptr<interface::Handler> m_handler_watch_only_changed; + interface::Node& m_node; + + CWallet *cwallet; bool fHaveWatchOnly; bool fForceCheckBalanceChanged; @@ -244,12 +230,7 @@ private: RecentRequestsTableModel *recentRequestsTableModel; // Cache some values to be able to detect changes - CAmount cachedBalance; - CAmount cachedUnconfirmedBalance; - CAmount cachedImmatureBalance; - CAmount cachedWatchOnlyBalance; - CAmount cachedWatchUnconfBalance; - CAmount cachedWatchImmatureBalance; + interface::WalletBalances m_cached_balances; EncryptionStatus cachedEncryptionStatus; int cachedNumBlocks; @@ -257,7 +238,7 @@ private: void subscribeToCoreSignals(); void unsubscribeFromCoreSignals(); - void checkBalanceChanged(); + void checkBalanceChanged(const interface::WalletBalances& new_balances); Q_SIGNALS: // Signal that balance in wallet changed diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp index 4df8a5687e..21bdfe3818 100644 --- a/src/qt/walletmodeltransaction.cpp +++ b/src/qt/walletmodeltransaction.cpp @@ -4,12 +4,11 @@ #include <qt/walletmodeltransaction.h> +#include <interface/node.h> #include <policy/policy.h> -#include <wallet/wallet.h> WalletModelTransaction::WalletModelTransaction(const QList<SendCoinsRecipient> &_recipients) : recipients(_recipients), - walletTransaction(0), fee(0) { } @@ -19,14 +18,14 @@ QList<SendCoinsRecipient> WalletModelTransaction::getRecipients() const return recipients; } -CTransactionRef& WalletModelTransaction::getTransaction() +std::unique_ptr<interface::PendingWalletTx>& WalletModelTransaction::getWtx() { - return walletTransaction; + return wtx; } unsigned int WalletModelTransaction::getTransactionSize() { - return (!walletTransaction ? 0 : ::GetVirtualTransactionSize(*walletTransaction)); + return wtx ? wtx->getVirtualSize() : 0; } CAmount WalletModelTransaction::getTransactionFee() const @@ -41,6 +40,7 @@ void WalletModelTransaction::setTransactionFee(const CAmount& newFee) void WalletModelTransaction::reassignAmounts(int nChangePosRet) { + const CTransaction* walletTransaction = &wtx->get(); int i = 0; for (QList<SendCoinsRecipient>::iterator it = recipients.begin(); it != recipients.end(); ++it) { @@ -80,13 +80,3 @@ CAmount WalletModelTransaction::getTotalTransactionAmount() const } return totalTransactionAmount; } - -void WalletModelTransaction::newPossibleKeyChange(CWallet *wallet) -{ - keyChange.reset(new CReserveKey(wallet)); -} - -CReserveKey *WalletModelTransaction::getPossibleKeyChange() -{ - return keyChange.get(); -} diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h index 931e960d18..52efafaf62 100644 --- a/src/qt/walletmodeltransaction.h +++ b/src/qt/walletmodeltransaction.h @@ -11,9 +11,10 @@ class SendCoinsRecipient; -class CReserveKey; -class CWallet; -class CWalletTx; +namespace interface { +class Node; +class PendingWalletTx; +} /** Data model for a walletmodel transaction. */ class WalletModelTransaction @@ -23,7 +24,7 @@ public: QList<SendCoinsRecipient> getRecipients() const; - CTransactionRef& getTransaction(); + std::unique_ptr<interface::PendingWalletTx>& getWtx(); unsigned int getTransactionSize(); void setTransactionFee(const CAmount& newFee); @@ -31,15 +32,11 @@ public: CAmount getTotalTransactionAmount() const; - void newPossibleKeyChange(CWallet *wallet); - CReserveKey *getPossibleKeyChange(); - void reassignAmounts(int nChangePosRet); // needed for the subtract-fee-from-amount feature private: QList<SendCoinsRecipient> recipients; - CTransactionRef walletTransaction; - std::unique_ptr<CReserveKey> keyChange; + std::unique_ptr<interface::PendingWalletTx> wtx; CAmount fee; }; diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index e1c892f115..1505557244 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -258,7 +258,7 @@ void WalletView::backupWallet() if (filename.isEmpty()) return; - if (!walletModel->backupWallet(filename)) { + if (!walletModel->wallet().backupWallet(filename.toLocal8Bit().data())) { Q_EMIT message(tr("Backup Failed"), tr("There was an error trying to save the wallet data to %1.").arg(filename), CClientUIInterface::MSG_ERROR); } |