Line data Source code
1 : // Copyright (c) 2011-2020 The Bitcoin Core developers
2 : // Distributed under the MIT software license, see the accompanying
3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 :
5 : #if defined(HAVE_CONFIG_H)
6 : #include <config/bitcoin-config.h>
7 : #endif
8 :
9 : #include <qt/walletmodel.h>
10 :
11 : #include <qt/addresstablemodel.h>
12 : #include <qt/clientmodel.h>
13 : #include <qt/guiconstants.h>
14 : #include <qt/guiutil.h>
15 : #include <qt/optionsmodel.h>
16 : #include <qt/paymentserver.h>
17 : #include <qt/recentrequeststablemodel.h>
18 : #include <qt/sendcoinsdialog.h>
19 : #include <qt/transactiontablemodel.h>
20 :
21 : #include <interfaces/handler.h>
22 : #include <interfaces/node.h>
23 : #include <key_io.h>
24 : #include <node/ui_interface.h>
25 : #include <psbt.h>
26 : #include <util/system.h> // for GetBoolArg
27 : #include <util/translation.h>
28 : #include <wallet/coincontrol.h>
29 : #include <wallet/wallet.h> // for CRecipient
30 :
31 : #include <stdint.h>
32 :
33 : #include <QDebug>
34 : #include <QMessageBox>
35 : #include <QSet>
36 : #include <QTimer>
37 :
38 :
39 0 : WalletModel::WalletModel(std::unique_ptr<interfaces::Wallet> wallet, ClientModel& client_model, const PlatformStyle *platformStyle, QObject *parent) :
40 0 : QObject(parent),
41 0 : m_wallet(std::move(wallet)),
42 0 : m_client_model(&client_model),
43 0 : m_node(client_model.node()),
44 0 : optionsModel(client_model.getOptionsModel()),
45 0 : addressTableModel(nullptr),
46 0 : transactionTableModel(nullptr),
47 0 : recentRequestsTableModel(nullptr),
48 0 : cachedEncryptionStatus(Unencrypted),
49 0 : timer(new QTimer(this))
50 0 : {
51 0 : fHaveWatchOnly = m_wallet->haveWatchOnly();
52 0 : addressTableModel = new AddressTableModel(this);
53 0 : transactionTableModel = new TransactionTableModel(platformStyle, this);
54 0 : recentRequestsTableModel = new RecentRequestsTableModel(this);
55 :
56 0 : subscribeToCoreSignals();
57 0 : }
58 :
59 0 : WalletModel::~WalletModel()
60 0 : {
61 0 : unsubscribeFromCoreSignals();
62 0 : }
63 :
64 0 : void WalletModel::startPollBalance()
65 : {
66 : // This timer will be fired repeatedly to update the balance
67 0 : connect(timer, &QTimer::timeout, this, &WalletModel::pollBalanceChanged);
68 0 : timer->start(MODEL_UPDATE_DELAY);
69 0 : }
70 :
71 0 : void WalletModel::setClientModel(ClientModel* client_model)
72 : {
73 0 : m_client_model = client_model;
74 0 : if (!m_client_model) timer->stop();
75 0 : }
76 :
77 0 : void WalletModel::updateStatus()
78 : {
79 0 : EncryptionStatus newEncryptionStatus = getEncryptionStatus();
80 :
81 0 : if(cachedEncryptionStatus != newEncryptionStatus) {
82 0 : Q_EMIT encryptionStatusChanged();
83 0 : }
84 0 : }
85 :
86 0 : void WalletModel::pollBalanceChanged()
87 : {
88 : // Avoid recomputing wallet balances unless a TransactionChanged or
89 : // BlockTip notification was received.
90 0 : if (!fForceCheckBalanceChanged && m_cached_last_update_tip == getLastBlockProcessed()) return;
91 :
92 : // Try to get balances and return early if locks can't be acquired. This
93 : // avoids the GUI from getting stuck on periodical polls if the core is
94 : // holding the locks for a longer time - for example, during a wallet
95 : // rescan.
96 0 : interfaces::WalletBalances new_balances;
97 0 : uint256 block_hash;
98 0 : if (!m_wallet->tryGetBalances(new_balances, block_hash)) {
99 0 : return;
100 : }
101 :
102 0 : if (fForceCheckBalanceChanged || block_hash != m_cached_last_update_tip) {
103 0 : fForceCheckBalanceChanged = false;
104 :
105 : // Balance and number of transactions might have changed
106 0 : m_cached_last_update_tip = block_hash;
107 :
108 0 : checkBalanceChanged(new_balances);
109 0 : if(transactionTableModel)
110 0 : transactionTableModel->updateConfirmations();
111 : }
112 0 : }
113 :
114 0 : void WalletModel::checkBalanceChanged(const interfaces::WalletBalances& new_balances)
115 : {
116 0 : if(new_balances.balanceChanged(m_cached_balances)) {
117 0 : m_cached_balances = new_balances;
118 0 : Q_EMIT balanceChanged(new_balances);
119 0 : }
120 0 : }
121 :
122 0 : void WalletModel::updateTransaction()
123 : {
124 : // Balance and number of transactions might have changed
125 0 : fForceCheckBalanceChanged = true;
126 0 : }
127 :
128 0 : void WalletModel::updateAddressBook(const QString &address, const QString &label,
129 : bool isMine, const QString &purpose, int status)
130 : {
131 0 : if(addressTableModel)
132 0 : addressTableModel->updateEntry(address, label, isMine, purpose, status);
133 0 : }
134 :
135 0 : void WalletModel::updateWatchOnlyFlag(bool fHaveWatchonly)
136 : {
137 0 : fHaveWatchOnly = fHaveWatchonly;
138 0 : Q_EMIT notifyWatchonlyChanged(fHaveWatchonly);
139 0 : }
140 :
141 0 : bool WalletModel::validateAddress(const QString &address)
142 : {
143 0 : return IsValidDestinationString(address.toStdString());
144 0 : }
145 :
146 0 : WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl& coinControl)
147 : {
148 0 : CAmount total = 0;
149 : bool fSubtractFeeFromAmount = false;
150 0 : QList<SendCoinsRecipient> recipients = transaction.getRecipients();
151 0 : std::vector<CRecipient> vecSend;
152 :
153 0 : if(recipients.empty())
154 : {
155 0 : return OK;
156 : }
157 :
158 0 : QSet<QString> setAddress; // Used to detect duplicates
159 0 : int nAddresses = 0;
160 :
161 : // Pre-check input data for validity
162 0 : for (const SendCoinsRecipient &rcp : recipients)
163 : {
164 0 : if (rcp.fSubtractFeeFromAmount)
165 0 : fSubtractFeeFromAmount = true;
166 : { // User-entered bitcoin address / amount:
167 0 : if(!validateAddress(rcp.address))
168 : {
169 0 : return InvalidAddress;
170 : }
171 0 : if(rcp.amount <= 0)
172 : {
173 0 : return InvalidAmount;
174 0 : }
175 0 : setAddress.insert(rcp.address);
176 0 : ++nAddresses;
177 :
178 0 : CScript scriptPubKey = GetScriptForDestination(DecodeDestination(rcp.address.toStdString()));
179 0 : CRecipient recipient = {scriptPubKey, rcp.amount, rcp.fSubtractFeeFromAmount};
180 0 : vecSend.push_back(recipient);
181 :
182 0 : total += rcp.amount;
183 0 : }
184 0 : }
185 0 : if(setAddress.size() != nAddresses)
186 : {
187 0 : return DuplicateAddress;
188 : }
189 :
190 0 : CAmount nBalance = m_wallet->getAvailableBalance(coinControl);
191 :
192 0 : if(total > nBalance)
193 : {
194 0 : return AmountExceedsBalance;
195 : }
196 :
197 : {
198 0 : CAmount nFeeRequired = 0;
199 0 : int nChangePosRet = -1;
200 0 : bilingual_str error;
201 :
202 0 : auto& newTx = transaction.getWtx();
203 0 : newTx = m_wallet->createTransaction(vecSend, coinControl, !wallet().privateKeysDisabled() /* sign */, nChangePosRet, nFeeRequired, error);
204 0 : transaction.setTransactionFee(nFeeRequired);
205 0 : if (fSubtractFeeFromAmount && newTx)
206 0 : transaction.reassignAmounts(nChangePosRet);
207 :
208 0 : if(!newTx)
209 : {
210 0 : if(!fSubtractFeeFromAmount && (total + nFeeRequired) > nBalance)
211 : {
212 0 : return SendCoinsReturn(AmountWithFeeExceedsBalance);
213 : }
214 0 : Q_EMIT message(tr("Send Coins"), QString::fromStdString(error.translated),
215 : CClientUIInterface::MSG_ERROR);
216 0 : return TransactionCreationFailed;
217 : }
218 :
219 : // Reject absurdly high fee. (This can never happen because the
220 : // wallet never creates transactions with fee greater than
221 : // m_default_max_tx_fee. This merely a belt-and-suspenders check).
222 0 : if (nFeeRequired > m_wallet->getDefaultMaxTxFee()) {
223 0 : return AbsurdFee;
224 : }
225 0 : }
226 :
227 0 : return SendCoinsReturn(OK);
228 0 : }
229 :
230 0 : WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &transaction)
231 : {
232 0 : QByteArray transaction_array; /* store serialized transaction */
233 :
234 : {
235 0 : std::vector<std::pair<std::string, std::string>> vOrderForm;
236 0 : for (const SendCoinsRecipient &rcp : transaction.getRecipients())
237 : {
238 0 : if (!rcp.message.isEmpty()) // Message from normal bitcoin:URI (bitcoin:123...?message=example)
239 0 : vOrderForm.emplace_back("Message", rcp.message.toStdString());
240 0 : }
241 :
242 0 : auto& newTx = transaction.getWtx();
243 0 : wallet().commitTransaction(newTx, {} /* mapValue */, std::move(vOrderForm));
244 :
245 0 : CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
246 0 : ssTx << *newTx;
247 0 : transaction_array.append(&(ssTx[0]), ssTx.size());
248 0 : }
249 :
250 : // Add addresses / update labels that we've sent to the address book,
251 : // and emit coinsSent signal for each recipient
252 0 : for (const SendCoinsRecipient &rcp : transaction.getRecipients())
253 : {
254 : {
255 0 : std::string strAddress = rcp.address.toStdString();
256 0 : CTxDestination dest = DecodeDestination(strAddress);
257 0 : std::string strLabel = rcp.label.toStdString();
258 : {
259 : // Check if we have a new address or an updated label
260 0 : std::string name;
261 0 : if (!m_wallet->getAddress(
262 : dest, &name, /* is_mine= */ nullptr, /* purpose= */ nullptr))
263 : {
264 0 : m_wallet->setAddressBook(dest, strLabel, "send");
265 0 : }
266 0 : else if (name != strLabel)
267 : {
268 0 : m_wallet->setAddressBook(dest, strLabel, ""); // "" means don't change purpose
269 0 : }
270 0 : }
271 0 : }
272 0 : Q_EMIT coinsSent(this, rcp, transaction_array);
273 0 : }
274 :
275 0 : checkBalanceChanged(m_wallet->getBalances()); // update balance immediately, otherwise there could be a short noticeable delay until pollBalanceChanged hits
276 :
277 0 : return SendCoinsReturn(OK);
278 0 : }
279 :
280 0 : OptionsModel *WalletModel::getOptionsModel()
281 : {
282 0 : return optionsModel;
283 : }
284 :
285 0 : AddressTableModel *WalletModel::getAddressTableModel()
286 : {
287 0 : return addressTableModel;
288 : }
289 :
290 0 : TransactionTableModel *WalletModel::getTransactionTableModel()
291 : {
292 0 : return transactionTableModel;
293 : }
294 :
295 0 : RecentRequestsTableModel *WalletModel::getRecentRequestsTableModel()
296 : {
297 0 : return recentRequestsTableModel;
298 : }
299 :
300 0 : WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const
301 : {
302 0 : if(!m_wallet->isCrypted())
303 : {
304 0 : return Unencrypted;
305 : }
306 0 : else if(m_wallet->isLocked())
307 : {
308 0 : return Locked;
309 : }
310 : else
311 : {
312 0 : return Unlocked;
313 : }
314 0 : }
315 :
316 0 : bool WalletModel::setWalletEncrypted(bool encrypted, const SecureString &passphrase)
317 : {
318 0 : if (encrypted) {
319 0 : return m_wallet->encryptWallet(passphrase);
320 : }
321 0 : return false;
322 0 : }
323 :
324 0 : bool WalletModel::setWalletLocked(bool locked, const SecureString &passPhrase)
325 : {
326 0 : if(locked)
327 : {
328 : // Lock
329 0 : return m_wallet->lock();
330 : }
331 : else
332 : {
333 : // Unlock
334 0 : return m_wallet->unlock(passPhrase);
335 : }
336 0 : }
337 :
338 0 : bool WalletModel::changePassphrase(const SecureString &oldPass, const SecureString &newPass)
339 : {
340 0 : m_wallet->lock(); // Make sure wallet is locked before attempting pass change
341 0 : return m_wallet->changeWalletPassphrase(oldPass, newPass);
342 : }
343 :
344 : // Handlers for core signals
345 0 : static void NotifyUnload(WalletModel* walletModel)
346 : {
347 0 : qDebug() << "NotifyUnload";
348 0 : bool invoked = QMetaObject::invokeMethod(walletModel, "unload");
349 0 : assert(invoked);
350 0 : }
351 :
352 0 : static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel)
353 : {
354 0 : qDebug() << "NotifyKeyStoreStatusChanged";
355 0 : bool invoked = QMetaObject::invokeMethod(walletmodel, "updateStatus", Qt::QueuedConnection);
356 0 : assert(invoked);
357 0 : }
358 :
359 0 : static void NotifyAddressBookChanged(WalletModel *walletmodel,
360 : const CTxDestination &address, const std::string &label, bool isMine,
361 : const std::string &purpose, ChangeType status)
362 : {
363 0 : QString strAddress = QString::fromStdString(EncodeDestination(address));
364 0 : QString strLabel = QString::fromStdString(label);
365 0 : QString strPurpose = QString::fromStdString(purpose);
366 :
367 0 : qDebug() << "NotifyAddressBookChanged: " + strAddress + " " + strLabel + " isMine=" + QString::number(isMine) + " purpose=" + strPurpose + " status=" + QString::number(status);
368 0 : bool invoked = QMetaObject::invokeMethod(walletmodel, "updateAddressBook", Qt::QueuedConnection,
369 0 : Q_ARG(QString, strAddress),
370 0 : Q_ARG(QString, strLabel),
371 0 : Q_ARG(bool, isMine),
372 0 : Q_ARG(QString, strPurpose),
373 0 : Q_ARG(int, status));
374 0 : assert(invoked);
375 0 : }
376 :
377 0 : static void NotifyTransactionChanged(WalletModel *walletmodel, const uint256 &hash, ChangeType status)
378 : {
379 : Q_UNUSED(hash);
380 : Q_UNUSED(status);
381 0 : bool invoked = QMetaObject::invokeMethod(walletmodel, "updateTransaction", Qt::QueuedConnection);
382 0 : assert(invoked);
383 0 : }
384 :
385 0 : static void ShowProgress(WalletModel *walletmodel, const std::string &title, int nProgress)
386 : {
387 : // emits signal "showProgress"
388 0 : bool invoked = QMetaObject::invokeMethod(walletmodel, "showProgress", Qt::QueuedConnection,
389 0 : Q_ARG(QString, QString::fromStdString(title)),
390 0 : Q_ARG(int, nProgress));
391 0 : assert(invoked);
392 0 : }
393 :
394 0 : static void NotifyWatchonlyChanged(WalletModel *walletmodel, bool fHaveWatchonly)
395 : {
396 0 : bool invoked = QMetaObject::invokeMethod(walletmodel, "updateWatchOnlyFlag", Qt::QueuedConnection,
397 0 : Q_ARG(bool, fHaveWatchonly));
398 0 : assert(invoked);
399 0 : }
400 :
401 0 : static void NotifyCanGetAddressesChanged(WalletModel* walletmodel)
402 : {
403 0 : bool invoked = QMetaObject::invokeMethod(walletmodel, "canGetAddressesChanged");
404 0 : assert(invoked);
405 0 : }
406 :
407 0 : void WalletModel::subscribeToCoreSignals()
408 : {
409 : // Connect signals to wallet
410 0 : m_handler_unload = m_wallet->handleUnload(std::bind(&NotifyUnload, this));
411 0 : m_handler_status_changed = m_wallet->handleStatusChanged(std::bind(&NotifyKeyStoreStatusChanged, this));
412 0 : m_handler_address_book_changed = m_wallet->handleAddressBookChanged(std::bind(NotifyAddressBookChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
413 0 : m_handler_transaction_changed = m_wallet->handleTransactionChanged(std::bind(NotifyTransactionChanged, this, std::placeholders::_1, std::placeholders::_2));
414 0 : m_handler_show_progress = m_wallet->handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2));
415 0 : m_handler_watch_only_changed = m_wallet->handleWatchOnlyChanged(std::bind(NotifyWatchonlyChanged, this, std::placeholders::_1));
416 0 : m_handler_can_get_addrs_changed = m_wallet->handleCanGetAddressesChanged(std::bind(NotifyCanGetAddressesChanged, this));
417 0 : }
418 :
419 0 : void WalletModel::unsubscribeFromCoreSignals()
420 : {
421 : // Disconnect signals from wallet
422 0 : m_handler_unload->disconnect();
423 0 : m_handler_status_changed->disconnect();
424 0 : m_handler_address_book_changed->disconnect();
425 0 : m_handler_transaction_changed->disconnect();
426 0 : m_handler_show_progress->disconnect();
427 0 : m_handler_watch_only_changed->disconnect();
428 0 : m_handler_can_get_addrs_changed->disconnect();
429 0 : }
430 :
431 : // WalletModel::UnlockContext implementation
432 0 : WalletModel::UnlockContext WalletModel::requestUnlock()
433 : {
434 0 : bool was_locked = getEncryptionStatus() == Locked;
435 0 : if(was_locked)
436 : {
437 : // Request UI to unlock wallet
438 0 : Q_EMIT requireUnlock();
439 0 : }
440 : // If wallet is still locked, unlock was failed or cancelled, mark context as invalid
441 0 : bool valid = getEncryptionStatus() != Locked;
442 :
443 0 : return UnlockContext(this, valid, was_locked);
444 0 : }
445 :
446 0 : WalletModel::UnlockContext::UnlockContext(WalletModel *_wallet, bool _valid, bool _relock):
447 0 : wallet(_wallet),
448 0 : valid(_valid),
449 0 : relock(_relock)
450 0 : {
451 0 : }
452 :
453 0 : WalletModel::UnlockContext::~UnlockContext()
454 0 : {
455 0 : if(valid && relock)
456 : {
457 0 : wallet->setWalletLocked(true);
458 0 : }
459 0 : }
460 :
461 0 : void WalletModel::UnlockContext::CopyFrom(UnlockContext&& rhs)
462 : {
463 : // Transfer context; old object no longer relocks wallet
464 0 : *this = rhs;
465 0 : rhs.relock = false;
466 0 : }
467 :
468 0 : void WalletModel::loadReceiveRequests(std::vector<std::string>& vReceiveRequests)
469 : {
470 0 : vReceiveRequests = m_wallet->getDestValues("rr"); // receive request
471 0 : }
472 :
473 0 : bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest)
474 : {
475 0 : CTxDestination dest = DecodeDestination(sAddress);
476 :
477 0 : std::stringstream ss;
478 0 : ss << nId;
479 0 : std::string key = "rr" + ss.str(); // "rr" prefix = "receive request" in destdata
480 :
481 0 : if (sRequest.empty())
482 0 : return m_wallet->eraseDestData(dest, key);
483 : else
484 0 : return m_wallet->addDestData(dest, key, sRequest);
485 0 : }
486 :
487 0 : bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
488 : {
489 0 : CCoinControl coin_control;
490 0 : coin_control.m_signal_bip125_rbf = true;
491 0 : std::vector<bilingual_str> errors;
492 0 : CAmount old_fee;
493 0 : CAmount new_fee;
494 0 : CMutableTransaction mtx;
495 0 : if (!m_wallet->createBumpTransaction(hash, coin_control, errors, old_fee, new_fee, mtx)) {
496 0 : QMessageBox::critical(nullptr, tr("Fee bump error"), tr("Increasing transaction fee failed") + "<br />(" +
497 0 : (errors.size() ? QString::fromStdString(errors[0].translated) : "") +")");
498 0 : return false;
499 : }
500 :
501 0 : const bool create_psbt = m_wallet->privateKeysDisabled();
502 :
503 : // allow a user based fee verification
504 0 : QString questionString = create_psbt ? tr("Do you want to draft a transaction with fee increase?") : tr("Do you want to increase the fee?");
505 0 : questionString.append("<br />");
506 0 : questionString.append("<table style=\"text-align: left;\">");
507 0 : questionString.append("<tr><td>");
508 0 : questionString.append(tr("Current fee:"));
509 0 : questionString.append("</td><td>");
510 0 : questionString.append(BitcoinUnits::formatHtmlWithUnit(getOptionsModel()->getDisplayUnit(), old_fee));
511 0 : questionString.append("</td></tr><tr><td>");
512 0 : questionString.append(tr("Increase:"));
513 0 : questionString.append("</td><td>");
514 0 : questionString.append(BitcoinUnits::formatHtmlWithUnit(getOptionsModel()->getDisplayUnit(), new_fee - old_fee));
515 0 : questionString.append("</td></tr><tr><td>");
516 0 : questionString.append(tr("New fee:"));
517 0 : questionString.append("</td><td>");
518 0 : questionString.append(BitcoinUnits::formatHtmlWithUnit(getOptionsModel()->getDisplayUnit(), new_fee));
519 0 : questionString.append("</td></tr></table>");
520 0 : SendConfirmationDialog confirmationDialog(tr("Confirm fee bump"), questionString);
521 0 : confirmationDialog.exec();
522 0 : QMessageBox::StandardButton retval = static_cast<QMessageBox::StandardButton>(confirmationDialog.result());
523 :
524 : // cancel sign&broadcast if user doesn't want to bump the fee
525 0 : if (retval != QMessageBox::Yes) {
526 0 : return false;
527 : }
528 :
529 0 : WalletModel::UnlockContext ctx(requestUnlock());
530 0 : if(!ctx.isValid())
531 : {
532 0 : return false;
533 : }
534 :
535 : // Short-circuit if we are returning a bumped transaction PSBT to clipboard
536 0 : if (create_psbt) {
537 0 : PartiallySignedTransaction psbtx(mtx);
538 0 : bool complete = false;
539 0 : const TransactionError err = wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete, nullptr);
540 0 : if (err != TransactionError::OK || complete) {
541 0 : QMessageBox::critical(nullptr, tr("Fee bump error"), tr("Can't draft transaction."));
542 0 : return false;
543 : }
544 : // Serialize the PSBT
545 0 : CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
546 0 : ssTx << psbtx;
547 0 : GUIUtil::setClipboard(EncodeBase64(ssTx.str()).c_str());
548 0 : Q_EMIT message(tr("PSBT copied"), "Copied to clipboard", CClientUIInterface::MSG_INFORMATION);
549 : return true;
550 0 : }
551 :
552 : // sign bumped transaction
553 0 : if (!m_wallet->signBumpTransaction(mtx)) {
554 0 : QMessageBox::critical(nullptr, tr("Fee bump error"), tr("Can't sign transaction."));
555 0 : return false;
556 : }
557 : // commit the bumped transaction
558 0 : if(!m_wallet->commitBumpTransaction(hash, std::move(mtx), errors, new_hash)) {
559 0 : QMessageBox::critical(nullptr, tr("Fee bump error"), tr("Could not commit transaction") + "<br />(" +
560 0 : QString::fromStdString(errors[0].translated)+")");
561 0 : return false;
562 : }
563 0 : return true;
564 0 : }
565 :
566 0 : bool WalletModel::isWalletEnabled()
567 : {
568 0 : return !gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET);
569 0 : }
570 :
571 0 : QString WalletModel::getWalletName() const
572 : {
573 0 : return QString::fromStdString(m_wallet->getWalletName());
574 0 : }
575 :
576 0 : QString WalletModel::getDisplayName() const
577 : {
578 0 : const QString name = getWalletName();
579 0 : return name.isEmpty() ? "["+tr("default wallet")+"]" : name;
580 0 : }
581 :
582 0 : bool WalletModel::isMultiwallet()
583 : {
584 0 : return m_node.walletClient().getWallets().size() > 1;
585 : }
586 :
587 0 : void WalletModel::refresh(bool pk_hash_only)
588 : {
589 0 : addressTableModel = new AddressTableModel(this, pk_hash_only);
590 0 : }
591 :
592 0 : uint256 WalletModel::getLastBlockProcessed() const
593 : {
594 0 : return m_client_model ? m_client_model->getBestBlockHash() : uint256{};
595 : }
|