2018-06-25 17:22:01 -04:00
// Copyright (c) 2011-2019 The Bitcoin Core developers
2014-12-13 01:09:33 -03:00
// Distributed under the MIT software license, see the accompanying
2013-11-04 12:20:43 -03:00
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-11-06 15:12:47 -03:00
# if defined(HAVE_CONFIG_H)
# include <config/bitcoin-config.h>
# endif
2017-11-09 21:57:53 -03:00
# include <qt/walletmodel.h>
# include <qt/addresstablemodel.h>
# include <qt/guiconstants.h>
2019-11-22 17:32:09 -03:00
# include <qt/guiutil.h>
2017-11-09 21:57:53 -03:00
# include <qt/optionsmodel.h>
# include <qt/paymentserver.h>
# include <qt/recentrequeststablemodel.h>
# include <qt/sendcoinsdialog.h>
# include <qt/transactiontablemodel.h>
2018-04-07 04:42:02 -03:00
# include <interfaces/handler.h>
# include <interfaces/node.h>
2017-09-19 22:12:25 -03:00
# include <key_io.h>
2017-11-09 21:57:53 -03:00
# include <ui_interface.h>
2018-10-22 19:51:11 -03:00
# include <util/system.h> // for GetBoolArg
2017-11-09 21:57:53 -03:00
# include <wallet/coincontrol.h>
2017-12-05 17:57:12 -03:00
# include <wallet/wallet.h> // for CRecipient
2011-06-30 12:05:29 -04:00
2013-04-13 02:13:08 -03:00
# include <stdint.h>
# include <QDebug>
2017-02-03 18:04:39 -03:00
# include <QMessageBox>
2011-07-16 13:01:05 -04:00
# include <QSet>
2012-07-05 11:43:28 -04:00
# include <QTimer>
2011-06-30 12:05:29 -04:00
2015-07-05 09:17:46 -03:00
2018-04-07 04:42:02 -03:00
WalletModel : : WalletModel ( std : : unique_ptr < interfaces : : Wallet > wallet , interfaces : : Node & node , const PlatformStyle * platformStyle , OptionsModel * _optionsModel , QObject * parent ) :
2018-07-30 06:37:09 -04:00
QObject ( parent ) , m_wallet ( std : : move ( wallet ) ) , m_node ( node ) , optionsModel ( _optionsModel ) , addressTableModel ( nullptr ) ,
transactionTableModel ( nullptr ) ,
recentRequestsTableModel ( nullptr ) ,
2012-07-05 11:43:28 -04:00
cachedEncryptionStatus ( Unencrypted ) ,
cachedNumBlocks ( 0 )
2011-06-30 12:05:29 -04:00
{
2017-04-17 19:56:44 -03:00
fHaveWatchOnly = m_wallet - > haveWatchOnly ( ) ;
2017-04-18 14:01:23 -03:00
addressTableModel = new AddressTableModel ( this ) ;
2017-04-18 17:42:30 -03:00
transactionTableModel = new TransactionTableModel ( platformStyle , this ) ;
recentRequestsTableModel = new RecentRequestsTableModel ( this ) ;
2012-05-06 13:40:58 -04:00
subscribeToCoreSignals ( ) ;
}
WalletModel : : ~ WalletModel ( )
{
unsubscribeFromCoreSignals ( ) ;
2011-06-30 12:05:29 -04:00
}
2019-10-12 18:26:47 -03:00
void WalletModel : : startPollBalance ( )
{
// This timer will be fired repeatedly to update the balance
QTimer * timer = new QTimer ( this ) ;
connect ( timer , & QTimer : : timeout , this , & WalletModel : : pollBalanceChanged ) ;
timer - > start ( MODEL_UPDATE_DELAY ) ;
}
2012-05-05 10:07:14 -04:00
void WalletModel : : updateStatus ( )
2011-06-30 12:05:29 -04:00
{
2012-05-05 10:07:14 -04:00
EncryptionStatus newEncryptionStatus = getEncryptionStatus ( ) ;
2016-10-24 04:21:51 -03:00
if ( cachedEncryptionStatus ! = newEncryptionStatus ) {
Q_EMIT encryptionStatusChanged ( ) ;
}
2012-05-05 10:07:14 -04:00
}
2012-07-05 11:43:28 -04:00
void WalletModel : : pollBalanceChanged ( )
2012-05-05 10:07:14 -04:00
{
2017-04-17 19:56:44 -03:00
// 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.
2018-04-07 04:42:02 -03:00
interfaces : : WalletBalances new_balances ;
2017-04-17 19:56:44 -03:00
int numBlocks = - 1 ;
if ( ! m_wallet - > tryGetBalances ( new_balances , numBlocks ) ) {
2014-04-23 03:40:48 -03:00
return ;
2017-04-17 19:56:44 -03:00
}
2014-04-23 03:40:48 -03:00
gui: Fix race in WalletModel::pollBalanceChanged
Poll function was wrongly setting cached height to the current chain height
instead of the chain height at the time of polling.
This bug could cause balances to appear out of date, and was first introduced
https://github.com/bitcoin/bitcoin/pull/10244/commits/a0704a8996bb950ae3c4d5b5a30e9dfe34cde1d3#r378452145
Before that commit, there wasn't a problem because cs_main was held during the
poll update.
Currently, the problem should be rare. But if
8937d99ce81a27ae5e1012a28323c0e26d89c50b from #17954 were merged, the problem
would get worse, because the wrong cachedNumBlocks value would be set if the
wallet was polled in the interval between a block being connected and it
processing the BlockConnected notification.
MarcoFalke <falke.marco@gmail.com> also points out that a0704a8996b could lead
to GUI hangs as well, because previously the pollBalanceChanged method, which
runs on the GUI thread, would only make a nonblocking TRY_LOCK(cs_main) call,
but after could make blocking LOCK(cs_main) calls, potentially locking up the
GUI.
Thanks to John Newbery <john@johnnewbery.com> for finding this bug this while
reviewing https://github.com/bitcoin/bitcoin/pull/17954.
2020-02-11 18:53:53 -03:00
if ( fForceCheckBalanceChanged | | numBlocks ! = cachedNumBlocks )
2012-07-10 09:19:57 -04:00
{
2014-08-16 20:34:42 -04:00
fForceCheckBalanceChanged = false ;
2014-04-23 03:40:48 -03:00
// Balance and number of transactions might have changed
gui: Fix race in WalletModel::pollBalanceChanged
Poll function was wrongly setting cached height to the current chain height
instead of the chain height at the time of polling.
This bug could cause balances to appear out of date, and was first introduced
https://github.com/bitcoin/bitcoin/pull/10244/commits/a0704a8996bb950ae3c4d5b5a30e9dfe34cde1d3#r378452145
Before that commit, there wasn't a problem because cs_main was held during the
poll update.
Currently, the problem should be rare. But if
8937d99ce81a27ae5e1012a28323c0e26d89c50b from #17954 were merged, the problem
would get worse, because the wrong cachedNumBlocks value would be set if the
wallet was polled in the interval between a block being connected and it
processing the BlockConnected notification.
MarcoFalke <falke.marco@gmail.com> also points out that a0704a8996b could lead
to GUI hangs as well, because previously the pollBalanceChanged method, which
runs on the GUI thread, would only make a nonblocking TRY_LOCK(cs_main) call,
but after could make blocking LOCK(cs_main) calls, potentially locking up the
GUI.
Thanks to John Newbery <john@johnnewbery.com> for finding this bug this while
reviewing https://github.com/bitcoin/bitcoin/pull/17954.
2020-02-11 18:53:53 -03:00
cachedNumBlocks = numBlocks ;
2014-04-23 03:40:48 -03:00
2017-04-17 19:56:44 -03:00
checkBalanceChanged ( new_balances ) ;
2014-04-15 12:38:25 -03:00
if ( transactionTableModel )
transactionTableModel - > updateConfirmations ( ) ;
2012-07-05 11:43:28 -04:00
}
}
2018-04-07 04:42:02 -03:00
void WalletModel : : checkBalanceChanged ( const interfaces : : WalletBalances & new_balances )
2012-07-05 11:43:28 -04:00
{
2017-04-17 19:56:44 -03:00
if ( new_balances . balanceChanged ( m_cached_balances ) ) {
m_cached_balances = new_balances ;
2018-03-31 07:41:33 -03:00
Q_EMIT balanceChanged ( new_balances ) ;
2012-07-05 11:43:28 -04:00
}
}
2012-02-14 08:08:00 -03:00
2014-10-28 15:52:21 -03:00
void WalletModel : : updateTransaction ( )
2012-07-05 11:43:28 -04:00
{
// Balance and number of transactions might have changed
2014-08-16 20:34:42 -04:00
fForceCheckBalanceChanged = true ;
2011-06-30 12:05:29 -04:00
}
2013-09-04 05:52:45 -04:00
void WalletModel : : updateAddressBook ( const QString & address , const QString & label ,
2013-08-29 10:19:43 -04:00
bool isMine , const QString & purpose , int status )
2012-03-24 14:48:18 -03:00
{
2012-05-05 10:07:14 -04:00
if ( addressTableModel )
2013-08-29 10:19:43 -04:00
addressTableModel - > updateEntry ( address , label , isMine , purpose , status ) ;
2012-03-24 14:48:18 -03:00
}
2014-07-26 15:05:11 -04:00
void WalletModel : : updateWatchOnlyFlag ( bool fHaveWatchonly )
{
fHaveWatchOnly = fHaveWatchonly ;
2015-07-14 08:59:05 -03:00
Q_EMIT notifyWatchonlyChanged ( fHaveWatchonly ) ;
2014-07-26 15:05:11 -04:00
}
2011-07-16 13:01:05 -04:00
bool WalletModel : : validateAddress ( const QString & address )
2011-06-30 12:05:29 -04:00
{
2017-08-22 22:02:33 -03:00
return IsValidDestinationString ( address . toStdString ( ) ) ;
2011-07-16 13:01:05 -04:00
}
2017-06-28 16:41:55 -04:00
WalletModel : : SendCoinsReturn WalletModel : : prepareTransaction ( WalletModelTransaction & transaction , const CCoinControl & coinControl )
2011-07-16 13:01:05 -04:00
{
2014-04-22 19:46:19 -03:00
CAmount total = 0 ;
2014-07-23 08:34:36 -04:00
bool fSubtractFeeFromAmount = false ;
2013-08-30 14:04:48 -04:00
QList < SendCoinsRecipient > recipients = transaction . getRecipients ( ) ;
2014-07-23 08:34:36 -04:00
std : : vector < CRecipient > vecSend ;
2011-07-16 13:01:05 -04:00
if ( recipients . empty ( ) )
2011-06-30 12:05:29 -04:00
{
2011-07-16 13:01:05 -04:00
return OK ;
2011-06-30 12:05:29 -04:00
}
2013-07-22 02:50:39 -04:00
QSet < QString > setAddress ; // Used to detect duplicates
int nAddresses = 0 ;
2011-07-16 13:01:05 -04:00
// Pre-check input data for validity
2017-06-01 21:25:02 -04:00
for ( const SendCoinsRecipient & rcp : recipients )
2011-06-30 12:05:29 -04:00
{
2014-07-23 08:34:36 -04:00
if ( rcp . fSubtractFeeFromAmount )
fSubtractFeeFromAmount = true ;
2013-07-22 02:50:39 -04:00
{ // User-entered bitcoin address / amount:
if ( ! validateAddress ( rcp . address ) )
{
return InvalidAddress ;
}
if ( rcp . amount < = 0 )
{
return InvalidAmount ;
}
setAddress . insert ( rcp . address ) ;
+ + nAddresses ;
2011-07-16 13:01:05 -04:00
2017-08-22 22:02:33 -03:00
CScript scriptPubKey = GetScriptForDestination ( DecodeDestination ( rcp . address . toStdString ( ) ) ) ;
2014-07-23 08:34:36 -04:00
CRecipient recipient = { scriptPubKey , rcp . amount , rcp . fSubtractFeeFromAmount } ;
vecSend . push_back ( recipient ) ;
2013-07-22 02:50:39 -04:00
total + = rcp . amount ;
2011-07-16 13:01:05 -04:00
}
2011-06-30 12:05:29 -04:00
}
2013-07-22 02:50:39 -04:00
if ( setAddress . size ( ) ! = nAddresses )
2011-07-16 13:01:05 -04:00
{
return DuplicateAddress ;
}
2017-04-17 19:56:44 -03:00
CAmount nBalance = m_wallet - > getAvailableBalance ( coinControl ) ;
2013-08-12 11:03:03 -04:00
if ( total > nBalance )
2011-06-30 12:05:29 -04:00
{
return AmountExceedsBalance ;
}
{
2014-04-22 19:46:19 -03:00
CAmount nFeeRequired = 0 ;
2014-07-23 08:34:36 -04:00
int nChangePosRet = - 1 ;
2013-04-25 18:31:22 -03:00
std : : string strFailReason ;
2013-08-30 14:04:48 -04:00
2017-04-17 19:56:44 -03:00
auto & newTx = transaction . getWtx ( ) ;
2017-12-05 17:57:12 -03:00
newTx = m_wallet - > createTransaction ( vecSend , coinControl , ! wallet ( ) . privateKeysDisabled ( ) /* sign */ , nChangePosRet , nFeeRequired , strFailReason ) ;
2013-08-30 14:04:48 -04:00
transaction . setTransactionFee ( nFeeRequired ) ;
2017-04-17 19:56:44 -03:00
if ( fSubtractFeeFromAmount & & newTx )
2014-07-23 08:34:36 -04:00
transaction . reassignAmounts ( nChangePosRet ) ;
2011-06-30 12:05:29 -04:00
2017-04-17 19:56:44 -03:00
if ( ! newTx )
2011-06-30 12:05:29 -04:00
{
2014-07-23 08:34:36 -04:00
if ( ! fSubtractFeeFromAmount & & ( total + nFeeRequired ) > nBalance )
2011-07-16 13:01:05 -04:00
{
2013-08-30 14:04:48 -04:00
return SendCoinsReturn ( AmountWithFeeExceedsBalance ) ;
2011-07-16 13:01:05 -04:00
}
2015-07-14 08:59:05 -03:00
Q_EMIT message ( tr ( " Send Coins " ) , QString : : fromStdString ( strFailReason ) ,
2013-04-25 18:31:22 -03:00
CClientUIInterface : : MSG_ERROR ) ;
2011-07-16 13:01:05 -04:00
return TransactionCreationFailed ;
2011-06-30 12:05:29 -04:00
}
2014-11-01 20:14:47 -03:00
2019-07-02 10:16:36 -04:00
// Reject absurdly high fee. (This can never happen because the
// wallet never creates transactions with fee greater than
// m_default_max_tx_fee. This merely a belt-and-suspenders check).
if ( nFeeRequired > m_wallet - > getDefaultMaxTxFee ( ) ) {
2015-01-30 23:54:55 -03:00
return AbsurdFee ;
2019-07-02 10:16:36 -04:00
}
2013-08-30 14:04:48 -04:00
}
return SendCoinsReturn ( OK ) ;
}
WalletModel : : SendCoinsReturn WalletModel : : sendCoins ( WalletModelTransaction & transaction )
{
QByteArray transaction_array ; /* store serialized transaction */
{
2017-02-02 17:30:03 -03:00
std : : vector < std : : pair < std : : string , std : : string > > vOrderForm ;
2017-06-01 21:25:02 -04:00
for ( const SendCoinsRecipient & rcp : transaction . getRecipients ( ) )
2013-07-22 02:50:39 -04:00
{
2017-11-06 15:12:47 -03:00
if ( ! rcp . message . isEmpty ( ) ) // Message from normal bitcoin:URI (bitcoin:123...?message=example)
2017-02-02 17:30:03 -03:00
vOrderForm . emplace_back ( " Message " , rcp . message . toStdString ( ) ) ;
2011-06-30 12:05:29 -04:00
}
2013-08-30 14:04:48 -04:00
2017-04-17 19:56:44 -03:00
auto & newTx = transaction . getWtx ( ) ;
2019-04-03 18:55:24 -03:00
wallet ( ) . commitTransaction ( newTx , { } /* mapValue */ , std : : move ( vOrderForm ) ) ;
2013-07-22 02:50:39 -04:00
CDataStream ssTx ( SER_NETWORK , PROTOCOL_VERSION ) ;
2019-07-18 12:06:23 -04:00
ssTx < < * newTx ;
2013-08-30 14:04:48 -04:00
transaction_array . append ( & ( ssTx [ 0 ] ) , ssTx . size ( ) ) ;
2011-06-30 12:05:29 -04:00
}
2017-06-19 18:57:31 -04:00
// Add addresses / update labels that we've sent to the address book,
2013-08-30 14:04:48 -04:00
// and emit coinsSent signal for each recipient
2017-06-01 21:25:02 -04:00
for ( const SendCoinsRecipient & rcp : transaction . getRecipients ( ) )
2011-06-30 12:05:29 -04:00
{
2011-07-16 13:01:05 -04:00
{
2013-10-08 09:23:57 -03:00
std : : string strAddress = rcp . address . toStdString ( ) ;
2017-08-22 22:02:33 -03:00
CTxDestination dest = DecodeDestination ( strAddress ) ;
2013-10-08 09:23:57 -03:00
std : : string strLabel = rcp . label . toStdString ( ) ;
2012-05-03 08:52:15 -04:00
{
2013-10-08 09:23:57 -03:00
// Check if we have a new address or an updated label
2017-04-17 19:56:44 -03:00
std : : string name ;
2018-04-10 12:50:10 -03:00
if ( ! m_wallet - > getAddress (
dest , & name , /* is_mine= */ nullptr , /* purpose= */ nullptr ) )
2013-10-08 09:23:57 -03:00
{
2017-04-17 19:56:44 -03:00
m_wallet - > setAddressBook ( dest , strLabel , " send " ) ;
2013-10-08 09:23:57 -03:00
}
2017-04-17 19:56:44 -03:00
else if ( name ! = strLabel )
2013-10-08 09:23:57 -03:00
{
2017-04-17 19:56:44 -03:00
m_wallet - > setAddressBook ( dest , strLabel , " " ) ; // "" means don't change purpose
2013-10-08 09:23:57 -03:00
}
2012-05-03 08:52:15 -04:00
}
2011-07-16 13:01:05 -04:00
}
2018-03-23 18:14:39 -03:00
Q_EMIT coinsSent ( this , rcp , transaction_array ) ;
2011-06-30 12:05:29 -04:00
}
2017-04-17 19:56:44 -03:00
checkBalanceChanged ( m_wallet - > getBalances ( ) ) ; // update balance immediately, otherwise there could be a short noticeable delay until pollBalanceChanged hits
2011-07-30 13:28:41 -04:00
2013-08-30 14:04:48 -04:00
return SendCoinsReturn ( OK ) ;
2011-06-30 12:05:29 -04:00
}
OptionsModel * WalletModel : : getOptionsModel ( )
{
return optionsModel ;
}
AddressTableModel * WalletModel : : getAddressTableModel ( )
{
return addressTableModel ;
}
TransactionTableModel * WalletModel : : getTransactionTableModel ( )
{
return transactionTableModel ;
}
2011-07-02 07:45:59 -04:00
2013-11-05 14:03:05 -03:00
RecentRequestsTableModel * WalletModel : : getRecentRequestsTableModel ( )
{
return recentRequestsTableModel ;
}
2011-08-23 15:08:42 -03:00
WalletModel : : EncryptionStatus WalletModel : : getEncryptionStatus ( ) const
{
2017-04-17 19:56:44 -03:00
if ( ! m_wallet - > isCrypted ( ) )
2011-08-23 15:08:42 -03:00
{
return Unencrypted ;
}
2017-04-17 19:56:44 -03:00
else if ( m_wallet - > isLocked ( ) )
2011-08-23 15:08:42 -03:00
{
return Locked ;
}
else
{
return Unlocked ;
}
}
2011-08-24 17:07:26 -03:00
2011-11-26 03:02:04 -03:00
bool WalletModel : : setWalletEncrypted ( bool encrypted , const SecureString & passphrase )
2011-08-24 17:07:26 -03:00
{
if ( encrypted )
{
// Encrypt
2017-04-17 19:56:44 -03:00
return m_wallet - > encryptWallet ( passphrase ) ;
2011-08-24 17:07:26 -03:00
}
else
{
// Decrypt -- TODO; not supported yet
return false ;
}
}
2011-11-26 03:02:04 -03:00
bool WalletModel : : setWalletLocked ( bool locked , const SecureString & passPhrase )
2011-08-24 17:07:26 -03:00
{
if ( locked )
{
// Lock
2017-04-17 19:56:44 -03:00
return m_wallet - > lock ( ) ;
2011-08-24 17:07:26 -03:00
}
else
{
// Unlock
2017-04-17 19:56:44 -03:00
return m_wallet - > unlock ( passPhrase ) ;
2011-08-24 17:07:26 -03:00
}
}
2011-11-26 03:02:04 -03:00
bool WalletModel : : changePassphrase ( const SecureString & oldPass , const SecureString & newPass )
2011-08-24 17:07:26 -03:00
{
2017-04-17 19:56:44 -03:00
m_wallet - > lock ( ) ; // Make sure wallet is locked before attempting pass change
return m_wallet - > changeWalletPassphrase ( oldPass , newPass ) ;
2012-02-14 09:14:43 -03:00
}
2012-05-06 13:40:58 -04:00
// Handlers for core signals
2018-06-05 06:17:28 -04:00
static void NotifyUnload ( WalletModel * walletModel )
{
qDebug ( ) < < " NotifyUnload " ;
2019-07-06 12:16:01 -04:00
bool invoked = QMetaObject : : invokeMethod ( walletModel , " unload " ) ;
assert ( invoked ) ;
2018-06-05 06:17:28 -04:00
}
2017-04-17 19:56:44 -03:00
static void NotifyKeyStoreStatusChanged ( WalletModel * walletmodel )
2012-05-06 13:40:58 -04:00
{
2013-09-04 05:52:45 -04:00
qDebug ( ) < < " NotifyKeyStoreStatusChanged " ;
2019-07-06 12:16:01 -04:00
bool invoked = QMetaObject : : invokeMethod ( walletmodel , " updateStatus " , Qt : : QueuedConnection ) ;
assert ( invoked ) ;
2012-05-06 13:40:58 -04:00
}
2017-04-17 19:56:44 -03:00
static void NotifyAddressBookChanged ( WalletModel * walletmodel ,
2013-08-29 10:19:43 -04:00
const CTxDestination & address , const std : : string & label , bool isMine ,
const std : : string & purpose , ChangeType status )
2012-05-06 13:40:58 -04:00
{
2017-08-22 22:02:33 -03:00
QString strAddress = QString : : fromStdString ( EncodeDestination ( address ) ) ;
2013-09-04 05:52:45 -04:00
QString strLabel = QString : : fromStdString ( label ) ;
QString strPurpose = QString : : fromStdString ( purpose ) ;
2015-01-08 07:44:25 -03:00
qDebug ( ) < < " NotifyAddressBookChanged: " + strAddress + " " + strLabel + " isMine= " + QString : : number ( isMine ) + " purpose= " + strPurpose + " status= " + QString : : number ( status ) ;
2019-07-06 12:16:01 -04:00
bool invoked = QMetaObject : : invokeMethod ( walletmodel , " updateAddressBook " , Qt : : QueuedConnection ,
2013-09-04 05:52:45 -04:00
Q_ARG ( QString , strAddress ) ,
Q_ARG ( QString , strLabel ) ,
2012-05-06 16:41:35 -04:00
Q_ARG ( bool , isMine ) ,
2013-09-04 05:52:45 -04:00
Q_ARG ( QString , strPurpose ) ,
2012-05-06 13:40:58 -04:00
Q_ARG ( int , status ) ) ;
2019-07-06 12:16:01 -04:00
assert ( invoked ) ;
2012-05-06 13:40:58 -04:00
}
2017-04-17 19:56:44 -03:00
static void NotifyTransactionChanged ( WalletModel * walletmodel , const uint256 & hash , ChangeType status )
2012-05-06 13:40:58 -04:00
{
2014-10-28 15:52:21 -03:00
Q_UNUSED ( hash ) ;
Q_UNUSED ( status ) ;
2019-07-06 12:16:01 -04:00
bool invoked = QMetaObject : : invokeMethod ( walletmodel , " updateTransaction " , Qt : : QueuedConnection ) ;
assert ( invoked ) ;
2012-05-06 13:40:58 -04:00
}
2014-03-18 20:26:14 -03:00
static void ShowProgress ( WalletModel * walletmodel , const std : : string & title , int nProgress )
{
2014-08-16 20:34:42 -04:00
// emits signal "showProgress"
2019-07-06 12:16:01 -04:00
bool invoked = QMetaObject : : invokeMethod ( walletmodel , " showProgress " , Qt : : QueuedConnection ,
2014-08-16 20:34:42 -04:00
Q_ARG ( QString , QString : : fromStdString ( title ) ) ,
Q_ARG ( int , nProgress ) ) ;
2019-07-06 12:16:01 -04:00
assert ( invoked ) ;
2014-03-18 20:26:14 -03:00
}
2014-07-26 15:05:11 -04:00
static void NotifyWatchonlyChanged ( WalletModel * walletmodel , bool fHaveWatchonly )
{
2019-07-06 12:16:01 -04:00
bool invoked = QMetaObject : : invokeMethod ( walletmodel , " updateWatchOnlyFlag " , Qt : : QueuedConnection ,
2014-07-26 15:05:11 -04:00
Q_ARG ( bool , fHaveWatchonly ) ) ;
2019-07-06 12:16:01 -04:00
assert ( invoked ) ;
2014-07-26 15:05:11 -04:00
}
2019-01-18 19:05:32 -03:00
static void NotifyCanGetAddressesChanged ( WalletModel * walletmodel )
{
2019-07-06 12:16:01 -04:00
bool invoked = QMetaObject : : invokeMethod ( walletmodel , " canGetAddressesChanged " ) ;
assert ( invoked ) ;
2019-01-18 19:05:32 -03:00
}
2012-05-06 13:40:58 -04:00
void WalletModel : : subscribeToCoreSignals ( )
{
// Connect signals to wallet
2018-10-17 12:51:17 -03:00
m_handler_unload = m_wallet - > handleUnload ( std : : bind ( & NotifyUnload , this ) ) ;
m_handler_status_changed = m_wallet - > handleStatusChanged ( std : : bind ( & NotifyKeyStoreStatusChanged , this ) ) ;
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 ) ) ;
m_handler_transaction_changed = m_wallet - > handleTransactionChanged ( std : : bind ( NotifyTransactionChanged , this , std : : placeholders : : _1 , std : : placeholders : : _2 ) ) ;
m_handler_show_progress = m_wallet - > handleShowProgress ( std : : bind ( ShowProgress , this , std : : placeholders : : _1 , std : : placeholders : : _2 ) ) ;
m_handler_watch_only_changed = m_wallet - > handleWatchOnlyChanged ( std : : bind ( NotifyWatchonlyChanged , this , std : : placeholders : : _1 ) ) ;
2019-01-18 19:05:32 -03:00
m_handler_can_get_addrs_changed = m_wallet - > handleCanGetAddressesChanged ( boost : : bind ( NotifyCanGetAddressesChanged , this ) ) ;
2012-05-06 13:40:58 -04:00
}
void WalletModel : : unsubscribeFromCoreSignals ( )
{
// Disconnect signals from wallet
2018-06-05 06:17:28 -04:00
m_handler_unload - > disconnect ( ) ;
2017-04-17 19:56:44 -03:00
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 ( ) ;
2019-01-18 19:05:32 -03:00
m_handler_can_get_addrs_changed - > disconnect ( ) ;
2012-05-06 13:40:58 -04:00
}
2011-08-24 17:07:26 -03:00
// WalletModel::UnlockContext implementation
WalletModel : : UnlockContext WalletModel : : requestUnlock ( )
{
bool was_locked = getEncryptionStatus ( ) = = Locked ;
if ( was_locked )
{
// Request UI to unlock wallet
2015-07-14 08:59:05 -03:00
Q_EMIT requireUnlock ( ) ;
2011-08-24 17:07:26 -03:00
}
// If wallet is still locked, unlock was failed or cancelled, mark context as invalid
bool valid = getEncryptionStatus ( ) ! = Locked ;
return UnlockContext ( this , valid , was_locked ) ;
}
2016-09-09 08:43:29 -03:00
WalletModel : : UnlockContext : : UnlockContext ( WalletModel * _wallet , bool _valid , bool _relock ) :
wallet ( _wallet ) ,
valid ( _valid ) ,
relock ( _relock )
2011-08-24 17:07:26 -03:00
{
}
WalletModel : : UnlockContext : : ~ UnlockContext ( )
{
if ( valid & & relock )
{
wallet - > setWalletLocked ( true ) ;
}
}
2019-05-09 21:07:33 -04:00
void WalletModel : : UnlockContext : : CopyFrom ( UnlockContext & & rhs )
2011-08-24 17:07:26 -03:00
{
// Transfer context; old object no longer relocks wallet
* this = rhs ;
rhs . relock = false ;
}
2013-08-12 11:03:03 -04:00
2014-01-14 01:05:43 -03:00
void WalletModel : : loadReceiveRequests ( std : : vector < std : : string > & vReceiveRequests )
{
2017-04-17 19:56:44 -03:00
vReceiveRequests = m_wallet - > getDestValues ( " rr " ) ; // receive request
2014-01-14 01:05:43 -03:00
}
bool WalletModel : : saveReceiveRequest ( const std : : string & sAddress , const int64_t nId , const std : : string & sRequest )
{
2017-08-22 22:02:33 -03:00
CTxDestination dest = DecodeDestination ( sAddress ) ;
2014-01-14 01:05:43 -03:00
std : : stringstream ss ;
ss < < nId ;
std : : string key = " rr " + ss . str ( ) ; // "rr" prefix = "receive request" in destdata
if ( sRequest . empty ( ) )
2017-04-17 19:56:44 -03:00
return m_wallet - > eraseDestData ( dest , key ) ;
2014-01-14 01:05:43 -03:00
else
2017-04-17 19:56:44 -03:00
return m_wallet - > addDestData ( dest , key , sRequest ) ;
2017-02-03 18:04:39 -03:00
}
2018-08-01 12:43:46 -04:00
bool WalletModel : : bumpFee ( uint256 hash , uint256 & new_hash )
2017-02-03 18:04:39 -03:00
{
2017-06-15 10:34:17 -04:00
CCoinControl coin_control ;
2018-04-07 13:12:46 -03:00
coin_control . m_signal_bip125_rbf = true ;
2017-06-15 10:34:17 -04:00
std : : vector < std : : string > errors ;
CAmount old_fee ;
CAmount new_fee ;
CMutableTransaction mtx ;
2017-04-17 19:56:44 -03:00
if ( ! m_wallet - > createBumpTransaction ( hash , coin_control , 0 /* totalFee */ , errors , old_fee , new_fee , mtx ) ) {
2018-07-31 14:02:34 -04:00
QMessageBox : : critical ( nullptr , tr ( " Fee bump error " ) , tr ( " Increasing transaction fee failed " ) + " <br />( " +
2017-06-15 10:34:17 -04:00
( errors . size ( ) ? QString : : fromStdString ( errors [ 0 ] ) : " " ) + " ) " ) ;
2017-02-03 18:04:39 -03:00
return false ;
}
2017-12-05 17:57:12 -03:00
const bool create_psbt = m_wallet - > privateKeysDisabled ( ) ;
2019-11-22 17:32:09 -03:00
2017-02-03 18:04:39 -03:00
// allow a user based fee verification
2019-11-22 17:32:09 -03:00
QString questionString = create_psbt ? tr ( " Do you want to draft a transaction with fee increase? " ) : tr ( " Do you want to increase the fee? " ) ;
2017-04-26 04:55:54 -03:00
questionString . append ( " <br /> " ) ;
questionString . append ( " <table style= \" text-align: left; \" > " ) ;
questionString . append ( " <tr><td> " ) ;
questionString . append ( tr ( " Current fee: " ) ) ;
questionString . append ( " </td><td> " ) ;
2017-06-15 10:34:17 -04:00
questionString . append ( BitcoinUnits : : formatHtmlWithUnit ( getOptionsModel ( ) - > getDisplayUnit ( ) , old_fee ) ) ;
2017-04-26 04:55:54 -03:00
questionString . append ( " </td></tr><tr><td> " ) ;
questionString . append ( tr ( " Increase: " ) ) ;
questionString . append ( " </td><td> " ) ;
2017-06-15 10:34:17 -04:00
questionString . append ( BitcoinUnits : : formatHtmlWithUnit ( getOptionsModel ( ) - > getDisplayUnit ( ) , new_fee - old_fee ) ) ;
2017-04-26 04:55:54 -03:00
questionString . append ( " </td></tr><tr><td> " ) ;
questionString . append ( tr ( " New fee: " ) ) ;
questionString . append ( " </td><td> " ) ;
2017-06-15 10:34:17 -04:00
questionString . append ( BitcoinUnits : : formatHtmlWithUnit ( getOptionsModel ( ) - > getDisplayUnit ( ) , new_fee ) ) ;
2017-04-26 04:55:54 -03:00
questionString . append ( " </td></tr></table> " ) ;
2017-02-03 18:04:39 -03:00
SendConfirmationDialog confirmationDialog ( tr ( " Confirm fee bump " ) , questionString ) ;
confirmationDialog . exec ( ) ;
2017-06-01 10:13:35 -04:00
QMessageBox : : StandardButton retval = static_cast < QMessageBox : : StandardButton > ( confirmationDialog . result ( ) ) ;
2017-02-03 18:04:39 -03:00
2018-03-18 11:26:45 -03:00
// cancel sign&broadcast if user doesn't want to bump the fee
2017-02-03 18:04:39 -03:00
if ( retval ! = QMessageBox : : Yes ) {
return false ;
}
WalletModel : : UnlockContext ctx ( requestUnlock ( ) ) ;
if ( ! ctx . isValid ( ) )
{
return false ;
}
2019-11-22 17:32:09 -03:00
// Short-circuit if we are returning a bumped transaction PSBT to clipboard
if ( create_psbt ) {
PartiallySignedTransaction psbtx ( mtx ) ;
bool complete = false ;
const TransactionError err = wallet ( ) . fillPSBT ( psbtx , complete , SIGHASH_ALL , false /* sign */ , true /* bip32derivs */ ) ;
2020-01-14 11:27:08 -03:00
if ( err ! = TransactionError : : OK | | complete ) {
QMessageBox : : critical ( nullptr , tr ( " Fee bump error " ) , tr ( " Can't draft transaction. " ) ) ;
return false ;
}
2019-11-22 17:32:09 -03:00
// Serialize the PSBT
CDataStream ssTx ( SER_NETWORK , PROTOCOL_VERSION ) ;
ssTx < < psbtx ;
GUIUtil : : setClipboard ( EncodeBase64 ( ssTx . str ( ) ) . c_str ( ) ) ;
Q_EMIT message ( tr ( " PSBT copied " ) , " Copied to clipboard " , CClientUIInterface : : MSG_INFORMATION ) ;
return true ;
}
2017-02-03 18:04:39 -03:00
// sign bumped transaction
2017-04-17 19:56:44 -03:00
if ( ! m_wallet - > signBumpTransaction ( mtx ) ) {
2018-07-31 14:02:34 -04:00
QMessageBox : : critical ( nullptr , tr ( " Fee bump error " ) , tr ( " Can't sign transaction. " ) ) ;
2017-02-03 18:04:39 -03:00
return false ;
}
// commit the bumped transaction
2018-08-01 12:43:46 -04:00
if ( ! m_wallet - > commitBumpTransaction ( hash , std : : move ( mtx ) , errors , new_hash ) ) {
2018-07-31 14:02:34 -04:00
QMessageBox : : critical ( nullptr , tr ( " Fee bump error " ) , tr ( " Could not commit transaction " ) + " <br />( " +
2017-06-15 10:34:17 -04:00
QString : : fromStdString ( errors [ 0 ] ) + " ) " ) ;
2017-02-03 18:04:39 -03:00
return false ;
}
return true ;
}
2016-09-21 07:37:00 -03:00
bool WalletModel : : isWalletEnabled ( )
{
2017-08-01 15:17:40 -04:00
return ! gArgs . GetBoolArg ( " -disablewallet " , DEFAULT_DISABLE_WALLET ) ;
2016-09-21 07:37:00 -03:00
}
2018-03-06 01:26:40 -03:00
QString WalletModel : : getWalletName ( ) const
{
2017-04-17 19:56:44 -03:00
return QString : : fromStdString ( m_wallet - > getWalletName ( ) ) ;
2018-03-06 01:26:40 -03:00
}
2018-06-25 17:22:01 -04:00
QString WalletModel : : getDisplayName ( ) const
{
const QString name = getWalletName ( ) ;
return name . isEmpty ( ) ? " [ " + tr ( " default wallet " ) + " ] " : name ;
}
2018-03-06 01:26:40 -03:00
bool WalletModel : : isMultiwallet ( )
{
2017-04-17 19:56:44 -03:00
return m_node . getWallets ( ) . size ( ) > 1 ;
2018-03-06 01:26:40 -03:00
}