1 // Copyright (c) 2011-2016 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.
5 #include "walletmodel.h"
7 #include "addresstablemodel.h"
8 #include "consensus/validation.h"
9 #include "guiconstants.h"
11 #include "optionsmodel.h"
12 #include "paymentserver.h"
13 #include "recentrequeststablemodel.h"
14 #include "sendcoinsdialog.h"
15 #include "transactiontablemodel.h"
20 #include "validation.h"
21 #include "net.h" // for g_connman
22 #include "policy/rbf.h"
24 #include "ui_interface.h"
25 #include "util.h" // for GetBoolArg
26 #include "wallet/feebumper.h"
27 #include "wallet/wallet.h"
28 #include "wallet/walletdb.h" // for BackupWallet
33 #include <QMessageBox>
37 #include <boost/foreach.hpp>
39 WalletModel::WalletModel(const PlatformStyle
*platformStyle
, CWallet
*_wallet
, OptionsModel
*_optionsModel
, QObject
*parent
) :
40 QObject(parent
), wallet(_wallet
), optionsModel(_optionsModel
), addressTableModel(0),
41 transactionTableModel(0),
42 recentRequestsTableModel(0),
43 cachedBalance(0), cachedUnconfirmedBalance(0), cachedImmatureBalance(0),
44 cachedEncryptionStatus(Unencrypted
),
47 fHaveWatchOnly
= wallet
->HaveWatchOnly();
48 fForceCheckBalanceChanged
= false;
50 addressTableModel
= new AddressTableModel(wallet
, this);
51 transactionTableModel
= new TransactionTableModel(platformStyle
, wallet
, this);
52 recentRequestsTableModel
= new RecentRequestsTableModel(wallet
, this);
54 // This timer will be fired repeatedly to update the balance
55 pollTimer
= new QTimer(this);
56 connect(pollTimer
, SIGNAL(timeout()), this, SLOT(pollBalanceChanged()));
57 pollTimer
->start(MODEL_UPDATE_DELAY
);
59 subscribeToCoreSignals();
62 WalletModel::~WalletModel()
64 unsubscribeFromCoreSignals();
67 CAmount
WalletModel::getBalance(const CCoinControl
*coinControl
) const
72 std::vector
<COutput
> vCoins
;
73 wallet
->AvailableCoins(vCoins
, true, coinControl
);
74 BOOST_FOREACH(const COutput
& out
, vCoins
)
76 nBalance
+= out
.tx
->tx
->vout
[out
.i
].nValue
;
81 return wallet
->GetBalance();
84 CAmount
WalletModel::getUnconfirmedBalance() const
86 return wallet
->GetUnconfirmedBalance();
89 CAmount
WalletModel::getImmatureBalance() const
91 return wallet
->GetImmatureBalance();
94 bool WalletModel::haveWatchOnly() const
96 return fHaveWatchOnly
;
99 CAmount
WalletModel::getWatchBalance() const
101 return wallet
->GetWatchOnlyBalance();
104 CAmount
WalletModel::getWatchUnconfirmedBalance() const
106 return wallet
->GetUnconfirmedWatchOnlyBalance();
109 CAmount
WalletModel::getWatchImmatureBalance() const
111 return wallet
->GetImmatureWatchOnlyBalance();
114 void WalletModel::updateStatus()
116 EncryptionStatus newEncryptionStatus
= getEncryptionStatus();
118 if(cachedEncryptionStatus
!= newEncryptionStatus
)
119 Q_EMIT
encryptionStatusChanged(newEncryptionStatus
);
122 void WalletModel::pollBalanceChanged()
124 // Get required locks upfront. This avoids the GUI from getting stuck on
125 // periodical polls if the core is holding the locks for a longer time -
126 // for example, during a wallet rescan.
127 TRY_LOCK(cs_main
, lockMain
);
130 TRY_LOCK(wallet
->cs_wallet
, lockWallet
);
134 if(fForceCheckBalanceChanged
|| chainActive
.Height() != cachedNumBlocks
)
136 fForceCheckBalanceChanged
= false;
138 // Balance and number of transactions might have changed
139 cachedNumBlocks
= chainActive
.Height();
141 checkBalanceChanged();
142 if(transactionTableModel
)
143 transactionTableModel
->updateConfirmations();
147 void WalletModel::checkBalanceChanged()
149 CAmount newBalance
= getBalance();
150 CAmount newUnconfirmedBalance
= getUnconfirmedBalance();
151 CAmount newImmatureBalance
= getImmatureBalance();
152 CAmount newWatchOnlyBalance
= 0;
153 CAmount newWatchUnconfBalance
= 0;
154 CAmount newWatchImmatureBalance
= 0;
157 newWatchOnlyBalance
= getWatchBalance();
158 newWatchUnconfBalance
= getWatchUnconfirmedBalance();
159 newWatchImmatureBalance
= getWatchImmatureBalance();
162 if(cachedBalance
!= newBalance
|| cachedUnconfirmedBalance
!= newUnconfirmedBalance
|| cachedImmatureBalance
!= newImmatureBalance
||
163 cachedWatchOnlyBalance
!= newWatchOnlyBalance
|| cachedWatchUnconfBalance
!= newWatchUnconfBalance
|| cachedWatchImmatureBalance
!= newWatchImmatureBalance
)
165 cachedBalance
= newBalance
;
166 cachedUnconfirmedBalance
= newUnconfirmedBalance
;
167 cachedImmatureBalance
= newImmatureBalance
;
168 cachedWatchOnlyBalance
= newWatchOnlyBalance
;
169 cachedWatchUnconfBalance
= newWatchUnconfBalance
;
170 cachedWatchImmatureBalance
= newWatchImmatureBalance
;
171 Q_EMIT
balanceChanged(newBalance
, newUnconfirmedBalance
, newImmatureBalance
,
172 newWatchOnlyBalance
, newWatchUnconfBalance
, newWatchImmatureBalance
);
176 void WalletModel::updateTransaction()
178 // Balance and number of transactions might have changed
179 fForceCheckBalanceChanged
= true;
182 void WalletModel::updateAddressBook(const QString
&address
, const QString
&label
,
183 bool isMine
, const QString
&purpose
, int status
)
185 if(addressTableModel
)
186 addressTableModel
->updateEntry(address
, label
, isMine
, purpose
, status
);
189 void WalletModel::updateWatchOnlyFlag(bool fHaveWatchonly
)
191 fHaveWatchOnly
= fHaveWatchonly
;
192 Q_EMIT
notifyWatchonlyChanged(fHaveWatchonly
);
195 bool WalletModel::validateAddress(const QString
&address
)
197 CBitcoinAddress
addressParsed(address
.toStdString());
198 return addressParsed
.IsValid();
201 WalletModel::SendCoinsReturn
WalletModel::prepareTransaction(WalletModelTransaction
&transaction
, const CCoinControl
*coinControl
)
204 bool fSubtractFeeFromAmount
= false;
205 QList
<SendCoinsRecipient
> recipients
= transaction
.getRecipients();
206 std::vector
<CRecipient
> vecSend
;
208 if(recipients
.empty())
213 QSet
<QString
> setAddress
; // Used to detect duplicates
216 // Pre-check input data for validity
217 Q_FOREACH(const SendCoinsRecipient
&rcp
, recipients
)
219 if (rcp
.fSubtractFeeFromAmount
)
220 fSubtractFeeFromAmount
= true;
222 if (rcp
.paymentRequest
.IsInitialized())
223 { // PaymentRequest...
224 CAmount subtotal
= 0;
225 const payments::PaymentDetails
& details
= rcp
.paymentRequest
.getDetails();
226 for (int i
= 0; i
< details
.outputs_size(); i
++)
228 const payments::Output
& out
= details
.outputs(i
);
229 if (out
.amount() <= 0) continue;
230 subtotal
+= out
.amount();
231 const unsigned char* scriptStr
= (const unsigned char*)out
.script().data();
232 CScript
scriptPubKey(scriptStr
, scriptStr
+out
.script().size());
233 CAmount nAmount
= out
.amount();
234 CRecipient recipient
= {scriptPubKey
, nAmount
, rcp
.fSubtractFeeFromAmount
};
235 vecSend
.push_back(recipient
);
239 return InvalidAmount
;
244 { // User-entered bitcoin address / amount:
245 if(!validateAddress(rcp
.address
))
247 return InvalidAddress
;
251 return InvalidAmount
;
253 setAddress
.insert(rcp
.address
);
256 CScript scriptPubKey
= GetScriptForDestination(CBitcoinAddress(rcp
.address
.toStdString()).Get());
257 CRecipient recipient
= {scriptPubKey
, rcp
.amount
, rcp
.fSubtractFeeFromAmount
};
258 vecSend
.push_back(recipient
);
263 if(setAddress
.size() != nAddresses
)
265 return DuplicateAddress
;
268 CAmount nBalance
= getBalance(coinControl
);
272 return AmountExceedsBalance
;
276 LOCK2(cs_main
, wallet
->cs_wallet
);
278 transaction
.newPossibleKeyChange(wallet
);
280 CAmount nFeeRequired
= 0;
281 int nChangePosRet
= -1;
282 std::string strFailReason
;
284 CWalletTx
*newTx
= transaction
.getTransaction();
285 CReserveKey
*keyChange
= transaction
.getPossibleKeyChange();
286 bool fCreated
= wallet
->CreateTransaction(vecSend
, *newTx
, *keyChange
, nFeeRequired
, nChangePosRet
, strFailReason
, coinControl
);
287 transaction
.setTransactionFee(nFeeRequired
);
288 if (fSubtractFeeFromAmount
&& fCreated
)
289 transaction
.reassignAmounts(nChangePosRet
);
293 if(!fSubtractFeeFromAmount
&& (total
+ nFeeRequired
) > nBalance
)
295 return SendCoinsReturn(AmountWithFeeExceedsBalance
);
297 Q_EMIT
message(tr("Send Coins"), QString::fromStdString(strFailReason
),
298 CClientUIInterface::MSG_ERROR
);
299 return TransactionCreationFailed
;
302 // reject absurdly high fee. (This can never happen because the
303 // wallet caps the fee at maxTxFee. This merely serves as a
304 // belt-and-suspenders check)
305 if (nFeeRequired
> maxTxFee
)
309 return SendCoinsReturn(OK
);
312 WalletModel::SendCoinsReturn
WalletModel::sendCoins(WalletModelTransaction
&transaction
)
314 QByteArray transaction_array
; /* store serialized transaction */
317 LOCK2(cs_main
, wallet
->cs_wallet
);
318 CWalletTx
*newTx
= transaction
.getTransaction();
320 Q_FOREACH(const SendCoinsRecipient
&rcp
, transaction
.getRecipients())
322 if (rcp
.paymentRequest
.IsInitialized())
324 // Make sure any payment requests involved are still valid.
325 if (PaymentServer::verifyExpired(rcp
.paymentRequest
.getDetails())) {
326 return PaymentRequestExpired
;
329 // Store PaymentRequests in wtx.vOrderForm in wallet.
330 std::string
key("PaymentRequest");
332 rcp
.paymentRequest
.SerializeToString(&value
);
333 newTx
->vOrderForm
.push_back(make_pair(key
, value
));
335 else if (!rcp
.message
.isEmpty()) // Message from normal bitcoin:URI (bitcoin:123...?message=example)
336 newTx
->vOrderForm
.push_back(make_pair("Message", rcp
.message
.toStdString()));
339 CReserveKey
*keyChange
= transaction
.getPossibleKeyChange();
340 CValidationState state
;
341 if(!wallet
->CommitTransaction(*newTx
, *keyChange
, g_connman
.get(), state
))
342 return SendCoinsReturn(TransactionCommitFailed
, QString::fromStdString(state
.GetRejectReason()));
344 CDataStream
ssTx(SER_NETWORK
, PROTOCOL_VERSION
);
346 transaction_array
.append(&(ssTx
[0]), ssTx
.size());
349 // Add addresses / update labels that we've sent to to the address book,
350 // and emit coinsSent signal for each recipient
351 Q_FOREACH(const SendCoinsRecipient
&rcp
, transaction
.getRecipients())
353 // Don't touch the address book when we have a payment request
354 if (!rcp
.paymentRequest
.IsInitialized())
356 std::string strAddress
= rcp
.address
.toStdString();
357 CTxDestination dest
= CBitcoinAddress(strAddress
).Get();
358 std::string strLabel
= rcp
.label
.toStdString();
360 LOCK(wallet
->cs_wallet
);
362 std::map
<CTxDestination
, CAddressBookData
>::iterator mi
= wallet
->mapAddressBook
.find(dest
);
364 // Check if we have a new address or an updated label
365 if (mi
== wallet
->mapAddressBook
.end())
367 wallet
->SetAddressBook(dest
, strLabel
, "send");
369 else if (mi
->second
.name
!= strLabel
)
371 wallet
->SetAddressBook(dest
, strLabel
, ""); // "" means don't change purpose
375 Q_EMIT
coinsSent(wallet
, rcp
, transaction_array
);
377 checkBalanceChanged(); // update balance immediately, otherwise there could be a short noticeable delay until pollBalanceChanged hits
379 return SendCoinsReturn(OK
);
382 OptionsModel
*WalletModel::getOptionsModel()
387 AddressTableModel
*WalletModel::getAddressTableModel()
389 return addressTableModel
;
392 TransactionTableModel
*WalletModel::getTransactionTableModel()
394 return transactionTableModel
;
397 RecentRequestsTableModel
*WalletModel::getRecentRequestsTableModel()
399 return recentRequestsTableModel
;
402 WalletModel::EncryptionStatus
WalletModel::getEncryptionStatus() const
404 if(!wallet
->IsCrypted())
408 else if(wallet
->IsLocked())
418 bool WalletModel::setWalletEncrypted(bool encrypted
, const SecureString
&passphrase
)
423 return wallet
->EncryptWallet(passphrase
);
427 // Decrypt -- TODO; not supported yet
432 bool WalletModel::setWalletLocked(bool locked
, const SecureString
&passPhrase
)
437 return wallet
->Lock();
442 return wallet
->Unlock(passPhrase
);
446 bool WalletModel::changePassphrase(const SecureString
&oldPass
, const SecureString
&newPass
)
450 LOCK(wallet
->cs_wallet
);
451 wallet
->Lock(); // Make sure wallet is locked before attempting pass change
452 retval
= wallet
->ChangeWalletPassphrase(oldPass
, newPass
);
457 bool WalletModel::backupWallet(const QString
&filename
)
459 return wallet
->BackupWallet(filename
.toLocal8Bit().data());
462 // Handlers for core signals
463 static void NotifyKeyStoreStatusChanged(WalletModel
*walletmodel
, CCryptoKeyStore
*wallet
)
465 qDebug() << "NotifyKeyStoreStatusChanged";
466 QMetaObject::invokeMethod(walletmodel
, "updateStatus", Qt::QueuedConnection
);
469 static void NotifyAddressBookChanged(WalletModel
*walletmodel
, CWallet
*wallet
,
470 const CTxDestination
&address
, const std::string
&label
, bool isMine
,
471 const std::string
&purpose
, ChangeType status
)
473 QString strAddress
= QString::fromStdString(CBitcoinAddress(address
).ToString());
474 QString strLabel
= QString::fromStdString(label
);
475 QString strPurpose
= QString::fromStdString(purpose
);
477 qDebug() << "NotifyAddressBookChanged: " + strAddress
+ " " + strLabel
+ " isMine=" + QString::number(isMine
) + " purpose=" + strPurpose
+ " status=" + QString::number(status
);
478 QMetaObject::invokeMethod(walletmodel
, "updateAddressBook", Qt::QueuedConnection
,
479 Q_ARG(QString
, strAddress
),
480 Q_ARG(QString
, strLabel
),
482 Q_ARG(QString
, strPurpose
),
486 static void NotifyTransactionChanged(WalletModel
*walletmodel
, CWallet
*wallet
, const uint256
&hash
, ChangeType status
)
491 QMetaObject::invokeMethod(walletmodel
, "updateTransaction", Qt::QueuedConnection
);
494 static void ShowProgress(WalletModel
*walletmodel
, const std::string
&title
, int nProgress
)
496 // emits signal "showProgress"
497 QMetaObject::invokeMethod(walletmodel
, "showProgress", Qt::QueuedConnection
,
498 Q_ARG(QString
, QString::fromStdString(title
)),
499 Q_ARG(int, nProgress
));
502 static void NotifyWatchonlyChanged(WalletModel
*walletmodel
, bool fHaveWatchonly
)
504 QMetaObject::invokeMethod(walletmodel
, "updateWatchOnlyFlag", Qt::QueuedConnection
,
505 Q_ARG(bool, fHaveWatchonly
));
508 void WalletModel::subscribeToCoreSignals()
510 // Connect signals to wallet
511 wallet
->NotifyStatusChanged
.connect(boost::bind(&NotifyKeyStoreStatusChanged
, this, _1
));
512 wallet
->NotifyAddressBookChanged
.connect(boost::bind(NotifyAddressBookChanged
, this, _1
, _2
, _3
, _4
, _5
, _6
));
513 wallet
->NotifyTransactionChanged
.connect(boost::bind(NotifyTransactionChanged
, this, _1
, _2
, _3
));
514 wallet
->ShowProgress
.connect(boost::bind(ShowProgress
, this, _1
, _2
));
515 wallet
->NotifyWatchonlyChanged
.connect(boost::bind(NotifyWatchonlyChanged
, this, _1
));
518 void WalletModel::unsubscribeFromCoreSignals()
520 // Disconnect signals from wallet
521 wallet
->NotifyStatusChanged
.disconnect(boost::bind(&NotifyKeyStoreStatusChanged
, this, _1
));
522 wallet
->NotifyAddressBookChanged
.disconnect(boost::bind(NotifyAddressBookChanged
, this, _1
, _2
, _3
, _4
, _5
, _6
));
523 wallet
->NotifyTransactionChanged
.disconnect(boost::bind(NotifyTransactionChanged
, this, _1
, _2
, _3
));
524 wallet
->ShowProgress
.disconnect(boost::bind(ShowProgress
, this, _1
, _2
));
525 wallet
->NotifyWatchonlyChanged
.disconnect(boost::bind(NotifyWatchonlyChanged
, this, _1
));
528 // WalletModel::UnlockContext implementation
529 WalletModel::UnlockContext
WalletModel::requestUnlock()
531 bool was_locked
= getEncryptionStatus() == Locked
;
534 // Request UI to unlock wallet
535 Q_EMIT
requireUnlock();
537 // If wallet is still locked, unlock was failed or cancelled, mark context as invalid
538 bool valid
= getEncryptionStatus() != Locked
;
540 return UnlockContext(this, valid
, was_locked
);
543 WalletModel::UnlockContext::UnlockContext(WalletModel
*_wallet
, bool _valid
, bool _relock
):
550 WalletModel::UnlockContext::~UnlockContext()
554 wallet
->setWalletLocked(true);
558 void WalletModel::UnlockContext::CopyFrom(const UnlockContext
& rhs
)
560 // Transfer context; old object no longer relocks wallet
565 bool WalletModel::getPubKey(const CKeyID
&address
, CPubKey
& vchPubKeyOut
) const
567 return wallet
->GetPubKey(address
, vchPubKeyOut
);
570 bool WalletModel::havePrivKey(const CKeyID
&address
) const
572 return wallet
->HaveKey(address
);
575 bool WalletModel::getPrivKey(const CKeyID
&address
, CKey
& vchPrivKeyOut
) const
577 return wallet
->GetKey(address
, vchPrivKeyOut
);
580 // returns a list of COutputs from COutPoints
581 void WalletModel::getOutputs(const std::vector
<COutPoint
>& vOutpoints
, std::vector
<COutput
>& vOutputs
)
583 LOCK2(cs_main
, wallet
->cs_wallet
);
584 BOOST_FOREACH(const COutPoint
& outpoint
, vOutpoints
)
586 if (!wallet
->mapWallet
.count(outpoint
.hash
)) continue;
587 int nDepth
= wallet
->mapWallet
[outpoint
.hash
].GetDepthInMainChain();
588 if (nDepth
< 0) continue;
589 COutput
out(&wallet
->mapWallet
[outpoint
.hash
], outpoint
.n
, nDepth
, true /* spendable */, true /* solvable */, true /* safe */);
590 vOutputs
.push_back(out
);
594 bool WalletModel::isSpent(const COutPoint
& outpoint
) const
596 LOCK2(cs_main
, wallet
->cs_wallet
);
597 return wallet
->IsSpent(outpoint
.hash
, outpoint
.n
);
600 // AvailableCoins + LockedCoins grouped by wallet address (put change in one group with wallet address)
601 void WalletModel::listCoins(std::map
<QString
, std::vector
<COutput
> >& mapCoins
) const
603 std::vector
<COutput
> vCoins
;
604 wallet
->AvailableCoins(vCoins
);
606 LOCK2(cs_main
, wallet
->cs_wallet
); // ListLockedCoins, mapWallet
607 std::vector
<COutPoint
> vLockedCoins
;
608 wallet
->ListLockedCoins(vLockedCoins
);
611 BOOST_FOREACH(const COutPoint
& outpoint
, vLockedCoins
)
613 if (!wallet
->mapWallet
.count(outpoint
.hash
)) continue;
614 int nDepth
= wallet
->mapWallet
[outpoint
.hash
].GetDepthInMainChain();
615 if (nDepth
< 0) continue;
616 COutput
out(&wallet
->mapWallet
[outpoint
.hash
], outpoint
.n
, nDepth
, true /* spendable */, true /* solvable */, true /* safe */);
617 if (outpoint
.n
< out
.tx
->tx
->vout
.size() && wallet
->IsMine(out
.tx
->tx
->vout
[outpoint
.n
]) == ISMINE_SPENDABLE
)
618 vCoins
.push_back(out
);
621 BOOST_FOREACH(const COutput
& out
, vCoins
)
625 while (wallet
->IsChange(cout
.tx
->tx
->vout
[cout
.i
]) && cout
.tx
->tx
->vin
.size() > 0 && wallet
->IsMine(cout
.tx
->tx
->vin
[0]))
627 if (!wallet
->mapWallet
.count(cout
.tx
->tx
->vin
[0].prevout
.hash
)) break;
628 cout
= COutput(&wallet
->mapWallet
[cout
.tx
->tx
->vin
[0].prevout
.hash
], cout
.tx
->tx
->vin
[0].prevout
.n
, 0 /* depth */, true /* spendable */, true /* solvable */, true /* safe */);
631 CTxDestination address
;
632 if(!out
.fSpendable
|| !ExtractDestination(cout
.tx
->tx
->vout
[cout
.i
].scriptPubKey
, address
))
634 mapCoins
[QString::fromStdString(CBitcoinAddress(address
).ToString())].push_back(out
);
638 bool WalletModel::isLockedCoin(uint256 hash
, unsigned int n
) const
640 LOCK2(cs_main
, wallet
->cs_wallet
);
641 return wallet
->IsLockedCoin(hash
, n
);
644 void WalletModel::lockCoin(COutPoint
& output
)
646 LOCK2(cs_main
, wallet
->cs_wallet
);
647 wallet
->LockCoin(output
);
650 void WalletModel::unlockCoin(COutPoint
& output
)
652 LOCK2(cs_main
, wallet
->cs_wallet
);
653 wallet
->UnlockCoin(output
);
656 void WalletModel::listLockedCoins(std::vector
<COutPoint
>& vOutpts
)
658 LOCK2(cs_main
, wallet
->cs_wallet
);
659 wallet
->ListLockedCoins(vOutpts
);
662 void WalletModel::loadReceiveRequests(std::vector
<std::string
>& vReceiveRequests
)
664 LOCK(wallet
->cs_wallet
);
665 BOOST_FOREACH(const PAIRTYPE(CTxDestination
, CAddressBookData
)& item
, wallet
->mapAddressBook
)
666 BOOST_FOREACH(const PAIRTYPE(std::string
, std::string
)& item2
, item
.second
.destdata
)
667 if (item2
.first
.size() > 2 && item2
.first
.substr(0,2) == "rr") // receive request
668 vReceiveRequests
.push_back(item2
.second
);
671 bool WalletModel::saveReceiveRequest(const std::string
&sAddress
, const int64_t nId
, const std::string
&sRequest
)
673 CTxDestination dest
= CBitcoinAddress(sAddress
).Get();
675 std::stringstream ss
;
677 std::string key
= "rr" + ss
.str(); // "rr" prefix = "receive request" in destdata
679 LOCK(wallet
->cs_wallet
);
680 if (sRequest
.empty())
681 return wallet
->EraseDestData(dest
, key
);
683 return wallet
->AddDestData(dest
, key
, sRequest
);
686 bool WalletModel::transactionCanBeAbandoned(uint256 hash
) const
688 LOCK2(cs_main
, wallet
->cs_wallet
);
689 const CWalletTx
*wtx
= wallet
->GetWalletTx(hash
);
690 if (!wtx
|| wtx
->isAbandoned() || wtx
->GetDepthInMainChain() > 0 || wtx
->InMempool())
695 bool WalletModel::abandonTransaction(uint256 hash
) const
697 LOCK2(cs_main
, wallet
->cs_wallet
);
698 return wallet
->AbandonTransaction(hash
);
701 bool WalletModel::transactionSignalsRBF(uint256 hash
) const
703 LOCK2(cs_main
, wallet
->cs_wallet
);
704 const CWalletTx
*wtx
= wallet
->GetWalletTx(hash
);
705 return wtx
&& SignalsOptInRBF(*wtx
);
708 bool WalletModel::bumpFee(uint256 hash
)
710 std::unique_ptr
<CFeeBumper
> feeBump
;
712 LOCK2(cs_main
, wallet
->cs_wallet
);
713 feeBump
.reset(new CFeeBumper(wallet
, hash
, nTxConfirmTarget
, false, 0, true));
715 if (feeBump
->getResult() != BumpFeeResult::OK
)
717 QMessageBox::critical(0, tr("Fee bump error"), tr("Increasing transaction fee failed") + "<br />(" +
718 (feeBump
->getErrors().size() ? QString::fromStdString(feeBump
->getErrors()[0]) : "") +")");
722 // allow a user based fee verification
723 QString questionString
= tr("Do you want to increase the fee?");
724 questionString
.append("<br />");
725 CAmount oldFee
= feeBump
->getOldFee();
726 CAmount newFee
= feeBump
->getNewFee();
727 questionString
.append("<table style=\"text-align: left;\">");
728 questionString
.append("<tr><td>");
729 questionString
.append(tr("Current fee:"));
730 questionString
.append("</td><td>");
731 questionString
.append(BitcoinUnits::formatHtmlWithUnit(getOptionsModel()->getDisplayUnit(), oldFee
));
732 questionString
.append("</td></tr><tr><td>");
733 questionString
.append(tr("Increase:"));
734 questionString
.append("</td><td>");
735 questionString
.append(BitcoinUnits::formatHtmlWithUnit(getOptionsModel()->getDisplayUnit(), newFee
- oldFee
));
736 questionString
.append("</td></tr><tr><td>");
737 questionString
.append(tr("New fee:"));
738 questionString
.append("</td><td>");
739 questionString
.append(BitcoinUnits::formatHtmlWithUnit(getOptionsModel()->getDisplayUnit(), newFee
));
740 questionString
.append("</td></tr></table>");
741 SendConfirmationDialog
confirmationDialog(tr("Confirm fee bump"), questionString
);
742 confirmationDialog
.exec();
743 QMessageBox::StandardButton retval
= (QMessageBox::StandardButton
)confirmationDialog
.result();
745 // cancel sign&broadcast if users doesn't want to bump the fee
746 if (retval
!= QMessageBox::Yes
) {
750 WalletModel::UnlockContext
ctx(requestUnlock());
756 // sign bumped transaction
759 LOCK2(cs_main
, wallet
->cs_wallet
);
760 res
= feeBump
->signTransaction(wallet
);
763 QMessageBox::critical(0, tr("Fee bump error"), tr("Can't sign transaction."));
766 // commit the bumped transaction
768 LOCK2(cs_main
, wallet
->cs_wallet
);
769 res
= feeBump
->commit(wallet
);
772 QMessageBox::critical(0, tr("Fee bump error"), tr("Could not commit transaction") + "<br />(" +
773 QString::fromStdString(feeBump
->getErrors()[0])+")");
779 bool WalletModel::isWalletEnabled()
781 return !GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET
);
784 bool WalletModel::hdEnabled() const
786 return wallet
->IsHDEnabled();
789 int WalletModel::getDefaultConfirmTarget() const
791 return nTxConfirmTarget
;
794 bool WalletModel::getDefaultWalletRbf() const